Нужно прочитать файл любого расширения по одному байту за один раз XE5 в динамический массив
Я уже пытался прочитать файл в TFileStream, но там я застрял, файл вставлен в TFileStream, но я не могу прочитать байты файла, я не запрограммирован некоторое время, пожалуйста, помогите мне.
Я также пытался прочитать его в обычный файл
var
myFile : File;
byteArray : array of byte;
oneByte : byte;
i, count : Integer;
begin
// Try to open the Test.byt file for writing to
AssignFile(myFile, 'C:\Users\theunie\Desktop\Toets\Test2.txt');
// Reopen the file for reading only
FileMode := fmOpenRead;
Reset(myFile, 1); // Now we define one record as 1 byte
// Display the file contents
// Start with a read of the first 6 bytes. 'count' is set to the
// actual number read
ShowMessage('Reading first set of bytes :');
setlength(ByteArray,sizeof(myfile));
BlockRead(myFile, byteArray, sizeof(myFile), count);
// Display the byte values read
for i := 0 to count do
ShowMessage(IntToStr(byteArray[i]));
// Now read one byte at a time to the end of the file
ShowMessage('Reading remaining bytes :');
while not Eof(myFile) do
begin
BlockRead(myFile, oneByte, 1); // Read and display one byte at a time
ShowMessage(IntToStr(oneByte));
end;
Freeandnil(byteArray);
// Close the file for the last time
CloseFile(myFile);
end;
так же как и это
procedure TForm1.Button1Click(Sender: TObject);
var
tf : TFileStream; //My Filestream
ar : array of byte;//The dynamic array I want to read it into
k : integer;//count
s : string;//I want to display this at the end
begin
k := 0;
tf := TFileStream.Create('C:\Users\Theunie\Desktop\Test2.txt',fmOpenReadwrite);
try
inc(k);
SetLength(ar,k);
ar[k-1] := tf.Read(ar[k-1],tf.size);
finally
s := inttostr(ar[0]) +';';
for k := 1 to length(ar) do
begin
s := s + ';' + IntToStr(ar[k]);
end;
FreeAndNil(ar);
end;
RichEdit1.Lines.Add(s);
end;
1 ответ
Файл большой? Может ли он поместиться в оперативную память одновременно?
В основном у вас есть два простых варианта создания 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
- http://docwiki.embarcadero.com/Libraries/XE8/en/System.IOUtils.TFile.ReadAllBytes
- http://www.malcolmgroves.com/blog/?p=447
- http://www.programdevelop.com/4641326/
- Почему функции System.IOUtils и TStreamReader используют fmShareCompat?
и так далее
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;
И наоборот, чтобы сохранить содержимое массива в файл, вы должны использовать что-то вроде Как преобразовать TBytes в двоичный файл? (используя MemoryStream)
Относительно ваших попыток.
http://wiki.freepascal.org/File_Handling_In_Pascal
как уже было отмечено выше,
SizeOf
не имеет отношения к файлам, то есть объем памятиFile
тип переменной. Если вы хотите придерживаться старого TurboPascal API, то вы должны использоватьFileSize
Функция, чтобы установить размер сразу. Для маленьких файлов это будет работать нормально, для больших файлов неправильный сам подход: "все сразу прочитать, потом обработать".inc(k); SetLength(ar,k);
- в цикле +1 - это очень плохая идея, это означает фрагментацию кучи, а также копирование, повторное копирование и повторное копирование буферного буфера данных снова и снова. То есть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;
Еще немного о динамических массивах: