является файл большой? Может ли он вписаться в оперативную память сразу?
У вас в основном есть две простые опции для создания DynArray из файла, но они рекомендуются только для файлов с малыми и средними значениями.
1: http://www.freepascal.org/docs-html/rtl/classes/tbytesstream.html
var BS: TBytesStream; b: byte; L, i: integer;
begin
BS := TBytesStream.Create;
try
BS.LoadFromFile('c:\boot.ini');
L := High(BS.Bytes);
for i := 0 to L do begin
b := BS.Bytes[i];
ShowMessage(IntToStr(b));
end;
finally
BS.Destroy;
end;
end;
2: использование IOUtils классы
и так далее
var BS: TBytes; b: byte; L, i: integer;
begin
BS := TFile.ReadAllBytes('c:\boot.ini');
L := High(BS);
for i := 0 to L do begin
b := BS[i];
ShowMessage(IntToStr(b));
end;
end;
С другой стороны, чтобы сохранить содержимое массива в файл, который вы бы использовать что-то вроде How to convert TBytes to Binary File? (using MemoryStream)
Что касается ваших попыток.
http://wiki.freepascal.org/File_Handling_In_Pascal
, как это уже было отмечено выше, SizeOf
не имеет никакого отношения к файлам, то есть размер памяти переменного типа File
. Если вы хотите придерживаться старого API TurboPascal, вам необходимо использовать функцию FileSize
, чтобы установить размер сразу. Для небольших файлов это будет хорошо, для больших файлов сам подход «сразу же считывает все данные в память, а затем обрабатывает».
inc(k); SetLength(ar,k);
- в цикле +1 - это очень плохая идея, это означает фрагментацию кучи и копирование и повторное копирование и повторное копирование буфера данных gorwing. То есть Length*Length/2
масштабирование, а также может быть сильно повреждающей структурой памяти кучи (google около heap fragmentation
).
Когда вы можете - вам нужно, прежде чем проверить FileSize
в и установить массив
FreeAndNil(byteArray);
- совершенно неправильно. Массивы не являются объектами. Вы не можете использовать Destroy/Free/FreeAndNil над ними. Как очистить динарму? Ну, вы можете просто ничего не делать, поскольку dynarrays - это один из типов с автоподтверждением, например, строки и интерфейсы и т. Д.Пока ваша процедура завершается, Delphi автоматически освобождает память от локальных переменных. Однако, если вы хотите, чтобы очистить dynarray в середине процедуры вы можете сделать это с помощью SetLength(MyDynArray, 0)
или ярлык MyDynArray := nil
BlockRead(myFile, byteArray, sizeof(myFile), count)
является неправильным на злоупотребления SizeOf
. Но у него есть и другая ошибка: ByteArray
переменная - это в основном указатель, поэтому это всего 4 (четыре!) Байта (8 байтов в коде Win64), поэтому вы просто перезаписываете весь стек вызовов. Вы действительно должны лучше использовать современный API-интерфейс типа. Но если вы хотите придерживаться старого небезопасного низкоуровневого API, тогда вам нужно быть предельно ясным на низкоуровневой реализации переменных разных типов. В основном вы хотите прочитать содержимое файла не в указатель на буфер, а в буфер, на который указывает, поэтому он должен быть как BlockRead(myFile, byteArray[0], MyFileAndArraySize, count)
. Затем, если была прочитана только часть вашего файла - count < MyFileAndArraySize
- вы бы BlockRead(myFile, byteArray[count], MyFileAndArraySize - count, count1)
, затем BlockRead(myFile, byteArray[count+count1], MyFileAndArraySize - count - count1, count2)
и так далее. Заботливо, даже если вы понимаете, как байты работают в низкоуровневых типах ...
ar[k-1] := tf.Read(ar[k-1],tf.size);
- это просто ужасно. Проверьте http://www.freepascal.org/docs-html/rtl/classes/tstream.read.html - результат - сколько байтов было действительно прочитано. Поэтому вместо заполнения массива содержимым файла вы чувствуете, что «сколько байтов было прочитано в одной попытке?» вместо. Вместо этого вы должны использовать процедуру tf.ReadBuffer
.
Но если вы хотите идти через части tf.Read
это должно быть что-то вроде
k := 0;
SetLength(ar, tf.Size);
while k < tf.Size do begin
k := k + tf.Read(ar[k], tfSize - k);
end;
Но опять же, у вас есть гораздо более простые инструменты для работы с небольшими файлами в современной Delphi
Еще одна проблема в вашем коде находится в
s := inttostr(ar[0]) +';';
for k := 1 to length(ar) do
begin
s := s + ';' + IntToStr(ar[k]);
end;
Это так называемая «разовая ошибка».
Хотя строки по историческим причинам индексируются от 1 до Length(s)
и обычно не имеют 0-го элемента, динамиков нет.
Динамические массивы индексируются от 0 = Low(ArrayVarName)
до High(ArrayVarName) = Length(ArrayVarName) - 1
. Таким образом, ваша петля пытается прочитать память за концом массива, вне самого массива.
Другая ошибка в том, что вы начинаете его с двумя точками с запятой, например «10 ;; 20; 30; 40 .....»
Это типично, когда вы устали или не очень внимателен. Поэтому вам лучше избегать индексирования массивов. Ниже рабочий код для включения динамического массива в строку из Delphi XE2
procedure TForm1.Button1Click(Sender: TObject);
var DynamicArray: TBytes;
SB: TStringBuilder; iSL: IJclStringList;
s1,s2: string; b: byte;
begin
DynamicArray := TBytes.Create(10, 20, 30, 40, 50);
SB := TStringBuilder.Create;
try
for b in DynamicArray do begin
if SB.Length > 0 then
SB.Append(';');
SB.Append(b);
end;
s1 := SB.ToString;
finally
SB.Destroy; // you must do it in Delphi for Windows
end;
iSL := JclStringList();
for b in DynamicArray do
iSL.Add(IntToStr(b));
s2 := iSL.Join(';');
iSL := nil; // you may skip it, Delphi would do on exit from procedure
ShowMessage('Dynamic array of bytes to string'
+ ^M^J' with Delphi RTL: ' + s1
+ ^M^J' with J.E.D.I. Code Library: ' + s2);
end;
Немного больше о динамических массивов:
'SetLength (ByteArray, sizeof (myfile)); ': function' sizeof (myfile) 'возвращает не размер файла, а размер переменной' myfile'. – Abelisto
Вам не нужно читать в массив dyn.Вам нужна строка с разделителями, поэтому нет необходимости в массиве. Почему вы просите написать файл, когда вы только читаете его. –