Главная » Delphi » Динамические массивы, строки и TMemoryStream

0

Читатель, несомненно, давно уже недоумевает: а зачем все это, если есть штука, которая так и называется — динамические массивы (они были введены в Delphi, начиная с версии 4)? Ну, во-первых, при побайтном поступлении больших чисел удобнее и быстрее механизма их преобразования, чем только что изложенный, я все равно не знаю (приближается к нему— по удобству— только механизм TStream, о котором далее). Но главное не в этом. Динамические массивы, в том числе строки типа string (которые отличаются от первых только тем, что в них не надо специально следить за текущей длиной, это делается автоматически), используют описанный ранее механизм динамического наращивания длины блоков памяти, и потому при больших объемах данных работают медленно. Утверждается, например, что время выполнения операции типа st:=st+chr (byte) пропорционально квадрату длины строки. Лучше уж использовать обычные массивы, но, на мой вкус, работа с указателями куда гибче и удобнее. Подчеркиваю — морочить себе этим всем голову стоит только при больших объемах данных, поступающих с большой скоростью. Иначе можно спокойно использовать любые механизмы.

Кстати, а как правильно прочесть нетипизированный файл в строку или динамический массив? Если вы просто напишете код типа:

var

st :string;

ff :file;

SetLength(st,1024); BlockRead(ff,st,length(st));

то получите ошибку, которую не всегда можно отследить даже через try.. .except. Это происходит потому, что st есть на самом деле указатель на строку, а не сама строка (в отличие от "родных" паскалевских строк типа ShortSring), и в этом смысле она похожа на pchar. И потому нечто запишется по указанному оператору только в то место, где хранятся 4 байта текущей длины. Правильно будет записать, например, так:

BlockRead(ff,PChar(st)л,length(st)) ;

Пример использования этой процедуры см. в демонстрационной программе Polsunok (глава 13). Чтение и запись строк, а также всякие их преобразования хорошо разобраны в [39].

Более эрудированный читатель, несомненно, от раздражения давно уже готов выбросить эту книжку в ближайший мусорный ящик, т. к. он знает, что в Delphi есть совершенно официальный механизм для чтения/записи потоковых данных ПОД названием TStream2 и его наследник TMernoryStream, специально "заточенный" под организацию массивов с произвольным доступом в памяти. Формально рассуждая, я должен был бы с него начать этот раздел. Доступ к данным при использовании Tstream осуществляется в точности, как к нетипизироваиным файлам (которые оказываются, таким образом, просто частным случаем потоковой модели), единственное различие в том, что в процедуре Read (Write) ДЛЯ ПОТОКа, В отличие от BlockRead (BlockWrite) ДЛЯ файла, не нужен четвертый (и там необязательный) параметр, возвращающий действительно прочитанное (записанное) количество блоков, оно возвращается через идентификатор заданного количества. Действительно, механизм довольно удобный. Вот как можно было бы осуществить процедуру побайтной записи и затем чтения чисел типа integer с использованием нашего экспериментального проекта. Пример заполнения массива несколько искусственный для наглядности: в первые четыре байта мы записываем комбинацию 255, 0, 0, 0, во вторые — 255, 255, 0, 0, и т. д., а потом по нажатию кнопки читаем уже четырехбайтные числа: var

х,i:integer; xb,yb:byte; Stream:TStream;

procedure TForml.FormCreate(Sender: TObject); begin

Stream:=TMemoryStream.Create; xb:=255;yb:=0; for i:=l to 16 do begin case i of

1,5,6,9,10,11/13,14,15,16: Stream.Write(xb,1); 2,3,4,7,8,12: Stream.Write(yb,1); end; end; end;

procedure TForml.ButtonlClick(Sender: TObject);

var st:string;

begin

Stream.Position:=0; Memol.Lines.Clear; for i:=l to 4 do begin Stream.Read(x,4); str (x,st);

Memol.Lines.Add(st); end; end;

procedure TForml.FormDestroy(Sender: TObject); begin

Stream.Free; end;

В результате нажатия на кнопку Buttonl мы получим в Memoi:

255 65535 16777215 -1

Если мы изменим тип х на dword, то получим вместо -I в последней строке 4 294 967 295. Здорово? Здорово, причем TStream имеет все полагающиеся свойства и методы для произвольного доступа к записанному потоку. Некоторые вы можете видеть в примере, кроме них есть еще и size, seek (смещение на нужное число байтов от начала или от текущей позиции) и очень удобный метод CopyFrom, который, например, позволяет копировать из потока TMemoryStream в поток TFilestream и обратно, обмениваясь таким образом данными из памяти с дисковым файлом.

Так почему же я не стал разбирать эту возможность с самого начала? Во- первых, потому что выделение памяти в TMemoryStream происходит с помощью функции GlobalReAiloc, совершенно аналогичной по механизму действия ReaiiocMem, да другого механизма придумать и нельзя, используем ли мы менеджер памяти Windows или Delphi, или даже свой собственный. Можно только оптимизировать выделение памяти под конкретную задачу, но сводиться это будет все равно к выбору, сколько лучше всего байтов зарезервировать заранее. Некоторые тонкости в работе различных менеджеров памяти мы тут, естественно, опускаем, потому что они, по большому счету, несущественны: самым быстрым способом останется захватить как можно больше, а там уж разбираться. И использовать ли тут указатели, динамические массивы или TMemoryStream— вопрос только удобства. Мы могли бы применять потоковый механизм (и даже просто блочное чтение из нетипизированного файла а-ля Pascal) вместо "маппинга" при чтении файла в программе Trace (см. главу 14), но в том конкретном случае это не ускорило бы работу — там нет потока данных, как такового, есть готовые файлы заранее известного размера. Отличие, кстати, реализации потокового механизма в С++ от Delphi (за что последнюю часто ругают) и заключается в том, что в первом случае управление потоками гораздо более гибкое и позволяет буферизовать обмен данными, например, при операциях с дисковыми файлами.

Во-вторых, и, пожалуй, в главных — пресловутая негибкость управления потоками проявляется не только в отсутствии механизмов регуляции выделения памяти. Сейчас мы сравним два метода сброса больших массивов памяти на диск и посмотрим, что быстрее, удобнее и "безглючнее".

Источник: Ревнч Ю. В.  Нестандартные приемы программирования на Delphi. — СПб.: БХВ-Петербург, 2005. — 560 е.: ил.

По теме:

  • Комментарии