Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2004.07.11;
Скачать: [xml.tar.bz2];

Вниз

Как лучше работать с 300-мегабайтным массивом ?   Найти похожие ветки 

 
Алекс А   (2004-06-21 02:45) [0]

Есть файл на диске "base1.bin" размером в 300 мегабайт, его надо загрузить в массив Base1 . Делают так :

 NewFile := TFileStream.Create("base1.bin", fmOpenRead or fmShareDenyWrite) ;
 try
   NewFile.ReadBuffer(Base1, 300000000);
 finally
   NewFile.Free;
 end;


Данные используются только для чтения. Загрузка длится более минуты. Хотелось бы, чтобы загрузка шла не сразу от и до, а как то постепенно, по мере обращения программы к отдельным частям массива, чтобы небыло этой минутной паузы. Можно ли это сделать ? Или как вообще лучше это организовать ?


 
default ©   (2004-06-21 02:57) [1]

"Можно ли это сделать ? Или как вообще лучше это организовать ?"
можно, для того чтобы сказать как лучше нужно больше информации


 
Fay   (2004-06-21 03:20) [2]

Он тебе весь в памяти нужен? Хоть раз. FileSeek (или SetFilePointer) не спасёт отца русской демократии?


 
default ©   (2004-06-21 03:25) [3]

Fay   (21.06.04 03:20) [2]
отца бы спосло


 
Алекс А   (2004-06-21 03:27) [4]


> Fay   (21.06.04 03:20) [2]
> Он тебе весь в памяти нужен? Хоть раз. FileSeek (или SetFilePointer)
> не спасёт отца русской демократии?

Примерно миллион чтений различных элементов в минуту. Правда не в одном месте, а больше по определённым небольшим областям. разбросанным по всему массиву.


 
Алекс А   (2004-06-21 03:33) [5]


> Fay   (21.06.04 03:20) [2]
> Он тебе весь в памяти нужен?

Я так подумал. Пожалуй весь он никогда не бывает нужен. Только частями, но которые находятся где попало.


 
Fay   (2004-06-21 03:39) [6]

Если подгружать постепенно, то, возможно, не получится сделать миллион ображений в минуту пока всё не загрузишь.


 
Fay   (2004-06-21 03:43) [7]

Можно зрузить мегабайтными () блоками и хранить массив с инфой о том, какие блоки уже загружены. А грузит лучше через CreateFile/SetFilePointer/ReadFile.


 
Алекс А   (2004-06-21 03:50) [8]


> Fay   (21.06.04 03:43) [7]
> Можно зрузить мегабайтными () блоками и хранить массив с
> инфой о том, какие блоки уже загружены. А грузит лучше через
> CreateFile/SetFilePointer/ReadFile.

Боюсь, что слишком гиморойно будет.
А нет ли какого нибудь менеджера памяти, который бы это сам делал? Чтобы просто проассоциировать массив с файлом на диске, и Дэльфи как нибудь сама бы это подгружала по мере необходимости, буферизировала бы, и всю прочую рутину сама бы выполняла?


 
Fay   (2004-06-21 03:53) [9]

CreateFileMapping ку?


 
Fay   (2004-06-21 03:55) [10]

Не, точно не ку 8)


 
Fay   (2004-06-21 03:59) [11]

В Delphi традиционно нет средств, работающик "как нибудь".
Всё работает совершенно конкретным образом.


> и всю прочую рутину сама бы выполняла


1) Так не бывает 8)
2) А зачем тогда ты?!


 
ЮЮ ©   (2004-06-21 04:05) [12]

А на компе памяти сколько? Не приводит ли такое считывание "в память" к записи в своп?


 
Fay   (2004-06-21 04:15) [13]

... ёжик задумался...
8)


 
Алекс А   (2004-06-21 04:20) [14]


> ЮЮ ©   (21.06.04 04:05) [12]
> А на компе памяти сколько? Не приводит ли такое считывание
> "в память" к записи в своп?

512 Мб. Не приводит. Но и не кэшируется. При повторном запуске приходится опять ждать минуту.


> Fay   (21.06.04 03:53) [9]
> CreateFileMapping ку?

Нашёл статью об этом : http://www.delphiworld.narod.ru/base/file_of_sop.html

Про него написано :
* TSharedStream работает правильно только с файлом подкачки,
  с обычным файлом проще и надежнее работать TFileStream"ом.

* Для тех кто знаком с File Mapping Functions"ами :
    Класс TSharedStream не может использоваться для синхронизации(разделения)
    данных среди различных процессов(программ/приложений). [пояснения в конструкторе]

* Класс TSharedStream можно рассматривать как альтернативу
  временным файлам (т.е. как замену TFileStream).
  Преимущество :
    а. Данные никто не сможет просмотреть.
    б. Страница, зарезервированная под данные, автомотически освобождается
       после уничтожения создавшего ее TSharedStream"а.

* Класс TSharedStream можно рассматривать как альтернативу
  TMemoryStream.
  Преимущество :
    а. Не надо опасаться нехватки памяти при большом объеме записываемых данных.
       [случай когда физически нехватает места на диске здесь не рассматривается].


И что это даёт ? Я и так не опасаюсь нехватки места на диске ...


 
Алекс А   (2004-06-21 04:35) [15]


> Fay   (21.06.04 03:59) [11]
> > и всю прочую рутину сама бы выполняла
>
> 1) Так не бывает 8)

Бывает, когда кто нибудь напишет. Пример - сама Виндос, которая кэширует запись/чтение на диск, причём так, что для пользователя это можно сказать незаметно.


> 2) А зачем тогда ты?!

Чтобы данные анализировать.


 
Fay   (2004-06-21 04:36) [16]

При повторном запуске чего?


 
Алекс А   (2004-06-21 04:46) [17]

procedure TSharedStream.LoadFromFile(const FileName: string);
var
Stream: TFileStream;
begin
Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
  LoadFromStream(Stream)
finally
  Stream.Free
end
end;


Это же мой код! Вон я в первом постинге его написал. Просто один в один. Стырили. :)
В общем как я понял SharedStream позволяет просто грузить массив по частям, причём чего загружено, а чего нет, думать самому. Слишком неудобно это как-то.


 
Алекс А   (2004-06-21 04:48) [18]


> Fay   (21.06.04 04:36) [16]
> При повторном запуске чего?

Программы конечно.


 
Fay   (2004-06-21 04:50) [19]


> Пример - сама Виндос, которая кэширует запись/чтение на
> диск, причём так, что для пользователя это можно сказать
> незаметно.

А что тебе говорит мозг, кеширует "Виндос" 300 метров, или нет?

Снова спрошу
При повторно запуске ЧЕГО надо избежать минутной задержки?!


 
Fay   (2004-06-21 04:53) [20]

Всё, уже вижу. 8)
А значения локальных переменных процедур "кешировать" не надо?!!!
Какой-то...
Удачи.


 
Алекс А   (2004-06-21 05:03) [21]

Ну, в общем я понял. Задача не имеет простого решения. А делать сложные я небуду.


 
Fay   (2004-06-21 08:12) [22]

Таких задач не бывает - "надо миллион раз в секунду кидаться в произвольное место 300-мегабайтного массива (чего-то?)".
Что на самом деле надо сделать, раз ты дошёл до такой жизни?


 
Романов Р.В. ©   (2004-06-21 08:38) [23]

Очень простое решение. Доставай нужную информацию прямо из файла с помощью CreateFileMapping или TFileStream. О производительности позаботятся кэш windows и HDD


 
Digitman ©   (2004-06-21 09:26) [24]


> Алекс А   (21.06.04 05:03) [21]
> Задача не имеет простого решения. А
> делать сложные я небуду.


Имеет.

Решение в таких случаях - отказ от собственного контейнера данных и движка управления ими в пользу одной из готовых существующих СУБД. Любая сколь-либо серьезная СУБД имеет готовые механизмы и интерфейс быстрого индексированного доступа к записям в таблицах БД любого размера, при этом таблица не считывается целиком в память, образуя набор данных, а считывается только позиционируемый фрагмент (одна или несколько последовательно идущих записей)


 
ChainikDenis ©   (2004-06-21 19:56) [25]

А ведь можно сразу к контроллера диска обращаться и считывать сектора - это ваще класно будет. Занял под файл определенные сектора, и читаешь какие только нужны..


 
MacroDenS ©   (2004-06-21 20:05) [26]

А вобще какой смысл так напрягать память и хард?
На кой тебе ворочить сразу 300 метров?
Лучше все же по чуть-чуть,
потому как ну загрузишь ты (пусть даже быстро) в память все 300 метров, а из памяти она в файл подкачки полезет...
Сделай что-то вроде метрового кэна, то что чаше загружается пусть сразу при запуске грузится, а остальное по мере необходимости пусть подгружается...
если ты за быстрый поиск бьешься, то здесь ты особо много не выиграешь...


 
jack128 ©   (2004-06-21 20:09) [27]


> Digitman ©   (21.06.04 09:26)

когда это БД была быстрее типизированного файла?

> Алекс А  

ты прав, простого решения ДЛЯ НАС не существует, мы не знаем специфику задачи...


 
Igorek ©   (2004-06-21 21:37) [28]

1) разбей файл на блоки (эмпиричеки определи принцип)
2) добавь к этому файлу еще один - файл приоритета загрузки - когда программа грузится, загружай только блоки, которые прописаны в файле приоритетов
3) при выходе из программы перестраивай файл приоритета в соотв. с протоколом работы с исходным файлом


 
Palladin ©   (2004-06-21 22:35) [29]


>  jack128 ©   (21.06.04 20:09)

Практически всегда когда необходимо отфильтровать данные, сделать определенную выборку при этом не съедая сотни мегабайт памяти и не заморачиваясь на технических решениях, сотнями раз реализованных другими...
ну и в конце концов чем DBF нетипизированный файл?


 
jack128 ©   (2004-06-21 22:58) [30]


> Практически всегда когда необходимо отфильтровать данные,
> сделать определенную выборку

гм. А автору это нужно? Пока что автор просит помочь закачать в память 300 метров, чтоб при этом интерфейс не тормозил ;-)

> ну и в конце концов чем DBF нетипизированный файл?
я не работал с ними, не знаю формата..


 
SergP ©   (2004-06-21 23:50) [31]

Ну а если так: При запуске проги ничего не загружать.
Разбиваем файл на несколько блоков (допустим как предлагали - мегабайтные блоки, или даже поменьше...)...
Если проге захотелось каких-то данных - определяем в каком они блоке и загружен ли в данный момент этот блок. Если они были  загружены ранее, если да используем то что загружено, если нет то загружаем этот блок...
?


 
Алекс А   (2004-06-22 02:25) [32]


> Fay   (21.06.04 08:12) [22]
> Таких задач не бывает - "надо миллион раз в секунду кидаться
> в произвольное место 300-мегабайтного массива (чего-то?)".


Зачем передёргиваете? Не в секунду, а в минуту Если вы лично не сталкивались с задачами, когда надо делать очень много проверок по базе, то это не значит, что таких задач несуществует.


> Романов Р.В. ©   (21.06.04 08:38) [23]
> Очень простое решение. Доставай нужную информацию прямо
> из файла с помощью CreateFileMapping или TFileStream. О
> производительности позаботятся кэш windows и HDD

Нет ли каких нибудь демок с этими FileMappingом?


> MacroDenS ©   (21.06.04 20:05) [26]
> Сделай что-то вроде метрового кэна, то что чаше загружается
> пусть сразу при запуске грузится, а остальное по мере необходимости
> пусть подгружается...

Что чаще загружается - никогда заранее не известно.


> Igorek ©   (21.06.04 21:37) [28]
> 1) разбей файл на блоки (эмпиричеки определи принцип)
> 2) добавь к этому файлу еще один - файл приоритета загрузки
> - когда программа грузится, загружай только блоки, которые
> прописаны в файле приоритетов
> 3) при выходе из программы перестраивай файл приоритета
> в соотв. с протоколом работы с исходным файлом

Овчинка выделки не стоит. Городить такой огород, чтобы слегка ускорить загрузку. Я лучше подожду минутку пока загрузится.


> SergP ©   (21.06.04 23:50) [31]
> Ну а если так: При запуске проги ничего не загружать.
> Разбиваем файл на несколько блоков (допустим как предлагали
> - мегабайтные блоки, или даже поменьше...)...
> Если проге захотелось каких-то данных

Ей это будет хотеться миллион раз в минуту. Добавить миллион проверок - значит добавить тормозов, что нежелательно.


 
Almaz ©   (2004-06-22 04:55) [33]


> Боюсь, что слишком гиморойно будет.
> А нет ли какого нибудь менеджера памяти, который бы это
> сам делал? Чтобы просто проассоциировать массив с файлом
> на диске, и Дэльфи как нибудь сама бы это подгружала по
> мере необходимости, буферизировала бы, и всю прочую рутину
> сама бы выполняла?

Это не менеджер памяти - это Вы очень точно описали тот самый MemoryMapping про который Вам уже не однократно говорили.
Вот простейший пример использования:
var
 MyArray: PChar;
 FileHandle: THandle;
 FileMap: THandle;
begin
 FileHandle := CreateFile("base1.bin", GENERIC_READ, 0, nil,
   OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, 0);
 if FileHandle <> INVALID_HANDLE_VALUE then
 begin
   FileMap := CreateFileMapping(FileHandle, nil, PAGE_READONLY, 0, 0, nil);
   if FileMap <> 0 then
   begin
     MyArray := MapViewOfFile(FileMap, FILE_MAP_READ, 0, 0, 0);
     if Assigned(MyArray) then
     try
      ...
       // Здесь можете работать с MyArray как с обычным массивом
       // только Read-only, как и было указано в задаче
       // все остальное возьмет на себя система.
       // ТОЛЬКО НЕ МЕНЯЙТЕ ЗНАЧЕНИЕ ПЕРЕМЕННОЙ MyArray

       // Пример использования:
       ShowMessage(MyArray[10245] + MyArray[1008874]);

      ...
     finally
       UnmapViewOfFile(MyArray);
       CloseHandle(FileMap);
       CloseHandle(FileHandle);
     end;
   end;
 end;
end;


Удачи.


 
Digitman ©   (2004-06-22 08:25) [34]


> jack128 ©   (21.06.04 20:09) [27]
> когда это БД была быстрее типизированного файла?


а нашута велосипед изобретать ?

большинство СУБД, может, и проигрывает незначительно в производительности движка, но зато дает готовый и удобный интерфейс для позиционирования и выборки ..


 
Anatoly Podgoretsky ©   (2004-06-22 08:45) [35]

Так как речь идет о файловых вещах, то базы типа дБейс по сути почти типизированая вещь, записи фиксированой длины, определенной структуры + заголовок. Конечно на произвольном доступе немного отстает, но за счет опережающего чтения во многих случаях быстрее. Но это не главная характеристика. А главное то, что отметил Digitman ©   (22.06.04 08:25) [34]


 
Digitman ©   (2004-06-22 08:57) [36]


> Anatoly Podgoretsky ©   (22.06.04 08:45) [35]


действительно, зачем изобретать собственный контейнерный формат и движок к нему, когда есть тот же DBF-формат и, например, CodeBase-движок к нему ? Если уж использование BDE (прямо перед носом !) претит ?


 
Igorek ©   (2004-06-22 10:45) [37]


> Digitman ©   (22.06.04 08:57) [36]

В целях обучения.


 
wicked ©   (2004-06-22 11:01) [38]

> Алекс А
memory mapped file - как раз то, что Вам нужно.... главное - не стремиться зарузить сразу все 300 мб файла в память...
в следующем моём постинге - код модуля с классом, наподобие TMemoryStream, только работает с memory mapped file...


 
wicked ©   (2004-06-22 11:01) [39]

unit wacStreams;

interface
uses Windows, SysUtils, Classes;

{$WEAKPACKAGEUNIT ON}

const
def_Granularity = (512 * 1024);

type
TMemoryMapStream = class(TCustomMemoryStream)
protected
 fRealSize: integer;
 fGranularity: integer;
 fFile: THandle;
 fMemMap: THandle;
 fReadOnly: boolean;

 procedure SetSize(const NewSize: int64); overload; override;
 procedure SetGranularity(value: integer); virtual;

 procedure OpenMMap; virtual;
 procedure CloseMMap; virtual;
 procedure Map; virtual;
 procedure UnMap; virtual;
 procedure doPack(reopen: boolean); virtual;
 procedure SetEOF(pos: integer); virtual;

public
 constructor Create(const AFileName: string; RequestReadOnly: boolean = false; AGranularity: integer = def_Granularity);
 destructor Destroy; override;

 procedure Clear;
 procedure SetSize(NewSize: integer); overload; override;
 function Write(const Buffer; Count: longint): longint; override;

 procedure LoadFromFile(const FileName: string); virtual;
 procedure LoadFromStream(Stream: TStream); virtual;
 procedure Pack; virtual;

 property Granularity: integer read fGranularity write SetGranularity;
 property ReadOnly: boolean read fReadOnly;
end;

implementation

const
min_size = 1024;

constructor TMemoryMapStream.Create(const AFileName: string; RequestReadOnly: boolean = false; AGranularity: integer = def_Granularity);
var filesize: integer;
begin
inherited Create;
SetPointer(nil, 0);
fRealSize:= 0;
SetGranularity(AGranularity);
fFile := 0;
fMemMap := 0;
fReadOnly := false;
try
 if not RequestReadOnly then
  fFile := CreateFile(
   PChar(AFileName),         // name
   GENERIC_READ or GENERIC_WRITE,      // desired access
   FILE_SHARE_READ,         // share mode (share read)
   nil,            // security attributes
   OPEN_ALWAYS,          // OPEN_EXISTING, // creation disposition
   FILE_ATTRIBUTE_NORMAL or FILE_FLAG_RANDOM_ACCESS, // flags and attributes
   0{nil});           // template file
 if (fFile = INVALID_HANDLE_VALUE) or RequestReadOnly then begin
  fFile := CreateFile(
   PChar(AFileName),         // name
   GENERIC_READ,          // desired access
   FILE_SHARE_READ,         // share mode (share read)
   nil,            // security attributes
   OPEN_EXISTING,          // creation disposition
   FILE_ATTRIBUTE_NORMAL or FILE_FLAG_RANDOM_ACCESS, // flags and attributes
   0{nil});           // template file
  if fFile = INVALID_HANDLE_VALUE then raise Exception.Create("could not open the file " + AFileName);
  fReadOnly := true;
    end;
    filesize := GetFileSize(fFile, nil);
 if filesize = 0 then SetEOF(min_size);
    OpenMMap;
 except
CloseMMap;
   if (fFile <> 0) and (fFile <> INVALID_HANDLE_VALUE) then CloseHandle(fFile);
   raise;
 end;
 Seek(0, soFromBeginning);
end;

destructor TMemoryMapStream.Destroy;
begin
if fFile <> 0 then begin
    doPack(false);
    CloseHandle(fFile);
   end;
end;

procedure TMemoryMapStream.OpenMMap;
begin
if fFile <> 0 then begin
 if fReadOnly then fMemMap := CreateFileMapping(fFile, nil, PAGE_READONLY or SEC_COMMIT, 0, 0, nil)
 else fMemMap := CreateFileMapping(fFile, nil, PAGE_READWRITE or SEC_COMMIT, 0, 0, nil);
 if fMemMap = 0 then raise Exception.Create("could not create memory map of file");
 Map;
end;
end;

procedure TMemoryMapStream.CloseMMap;
begin
if fMemMap <> 0 then begin
       UnMap;
       CloseHandle(fMemMap);
end;
end;

procedure TMemoryMapStream.Map;
var p: pointer;
filesize: integer;
begin
   SetPointer(nil, 0);
   if fReadOnly then
       p := MapViewOfFile(fMemMap, FILE_MAP_READ, 0, 0, 0)
else
 p := MapViewOfFile(fMemMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if p = nil then raise Exception.Create("could not read file");
filesize := GetFileSize(fFile, nil);
fRealSize := filesize;
SetPointer(p, filesize);
Position := 0;
end;

procedure TMemoryMapStream.UnMap;
begin
if Memory <> nil then begin
 if not fReadOnly then FlushViewOfFile(Memory, 0);
 UnmapViewOfFile(Memory);
end;
end;

procedure TMemoryMapStream.doPack(reopen: boolean);
begin
CloseMMap;
try
 SetEOF(Size);
   finally
    if reopen then OpenMMap;
end;
end;

procedure TMemoryMapStream.SetEOF(pos: integer);
begin
   if fReadOnly then exit;
   if SetFilePointer(fFile, pos, nil, FILE_BEGIN) = DWORD(-1) then // !!! INVALID_SET_FILE_POINTER
    raise Exception.Create("could not resize file");
   SetEndOfFile(fFile);
end;

//---------------------------------------------------------------------------

procedure TMemoryMapStream.SetSize(const NewSize: int64);
begin
SetSize(integer(NewSize));
end;

procedure TMemoryMapStream.SetGranularity(value: integer);
begin
if value = fGranularity then exit;
   fGranularity := value;
   if fGranularity < min_size then fGranularity := min_size;
end;

procedure TMemoryMapStream.Clear;
begin
   SetSize(min_size);
   doPack(true);
   ZeroMemory(Memory, min_size);
end;

procedure TMemoryMapStream.SetSize(NewSize: integer);
var mult: integer;
begin
if NewSize = Size then exit;
if (fFile <> 0) and not fReadOnly then begin
 if NewSize > fRealSize then begin
  CloseMMap;
  mult := (NewSize - fRealSize) div fGranularity;
           if ((NewSize - fRealSize) mod fGranularity) > 0 then
            fRealSize := fRealSize + ((mult + 1) * fGranularity)
  else
            fRealSize := fRealSize + (mult * fGranularity);
  SetEOF(fRealSize);
           OpenMMap;
  SetPointer(Memory, NewSize);
 end
 else SetPointer(Memory, NewSize);
end;
end;

function TMemoryMapStream.Write(const Buffer; Count: longint): longint;
var pos: longint;
begin
pos := Position;
if (pos < 0) or (Count <= 0) or fReadOnly then begin
 result := 0;
 exit;
   end;
if (pos + Count) > Size then SetSize(pos + Count);
System.Move(Buffer, (pointer(longint(Memory) + pos))^, Count);
pos := pos + Count;
Position := pos;
   Result := Count;
end;

procedure TMemoryMapStream.LoadFromFile(const FileName: string);
var f: TFileStream;
begin
   f := TFileStream.Create(FileName, fmOpenRead);
   try
    LoadFromStream(f);
finally
    f.Free;
end;
end;

procedure TMemoryMapStream.LoadFromStream(Stream: TStream);
var sz: integer;
begin
if fReadOnly then exit;
sz := Stream.Size;
SetSize(sz);
Stream.Read(Memory^, sz);
Seek(0, soFromBeginning);
end;

procedure TMemoryMapStream.Pack;
begin
doPack(true);
end;

end.


 
wicked ©   (2004-06-22 11:02) [40]

упс... а куда ушло форматирование?... :(



Страницы: 1 2 вся ветка

Форум: "Основная";
Текущий архив: 2004.07.11;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.59 MB
Время: 0.035 c
6-1084085778
Dublicator
2004-05-09 10:56
2004.07.11
Передача изображения по локальной сети


14-1087717818
Anatoly Podgoretsky
2004-06-20 11:50
2004.07.11
С днем рождения 19.06.2004


1-1088591359
BiSido
2004-06-30 14:29
2004.07.11
Как поменять из верхнего регистра в нижний (русские буквы)? (+)


3-1086951857
kondryuk
2004-06-11 15:04
2004.07.11
Выход за пределы диапазона


14-1088146776
Nikolay M.
2004-06-25 10:59
2004.07.11
А из Твери есть кто?





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский