Нужно прочитать файл любого расширения по одному байту за один раз 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

и так далее

 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

  1. как уже было отмечено выше, SizeOf не имеет отношения к файлам, то есть объем памяти File тип переменной. Если вы хотите придерживаться старого TurboPascal API, то вы должны использовать FileSize Функция, чтобы установить размер сразу. Для маленьких файлов это будет работать нормально, для больших файлов неправильный сам подход: "все сразу прочитать, потом обработать".

  2. inc(k); SetLength(ar,k); - в цикле +1 - это очень плохая идея, это означает фрагментацию кучи, а также копирование, повторное копирование и повторное копирование буферного буфера данных снова и снова. То есть Length*Length/2 масштабирование, а также может сильно повредить структуру памяти кучи (Google о heap fragmentation).
    Когда вы можете - вам нужно проверить FileSize до и установите массив в

  3. FreeAndNil(byteArray); - совершенно неправильно. Массивы не являются объектами. Вы не можете использовать Destroy / Free / FreeAndNil над ними. Как тогда почистить динар? Что ж, вы можете просто ничего не делать, поскольку dynarrays - это один из типов с автоматическим пересчетом, таких как строки и интерфейсы и т. Д. До тех пор, пока ваша процедура завершается, Delphi автоматически освобождает память от локальных переменных. Однако если вы хотите очистить dynarray в середине процедуры, вы можете сделать это через SetLength( MyDynArray, 0 ) или ярлык MyDynArray := nil

  4. 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) и так далее. Утомительно, даже если вы поймете, как бегают байты в низкоуровневых типах...

  5. 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;

Еще немного о динамических массивах:

Другие вопросы по тегам