需要读取任何扩展名的文件,一次一个字节 XE5 到动态数组中
Need to read a file, of any extension one byte at a time XE5 into dynamic array
我已经尝试将文件读入 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;
文件很大吗?它可以一次放入 RAM 中吗?
您基本上有两个简单的选项可以从文件中创建 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/
- Why do the System.IOUtils functions and TStreamReader use 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;
相反,要将数组的内容保存到文件中,您可以使用 How to convert TBytes to Binary File? (using MemoryStream)
关于您的尝试。
http://wiki.freepascal.org/File_Handling_In_Pascal
如前所述,SizeOf
与文件无关,即File
变量类型的内存大小。如果你想坚持使用旧的 TurboPascal API,那么你必须立即使用 FileSize
函数来设置大小。对于小文件,它可以正常工作,对于大文件,这种方法 "read it all to memory at once, then process" 是错误的。
inc(k); SetLength(ar,k);
- 在 +1 循环中 - 这是一个非常糟糕的主意,这意味着堆碎片和复制、重新复制和重新复制 gorwing 数据缓冲时间然后再次。那是 Length*Length/2
缩放,也可能严重破坏堆内存结构(google 大约 heap fragmentation
)。
如果可以 - 您需要检查之前的 FileSize
并将数组设置为
FreeAndNil(byteArray);
- 完全错误。数组不是对象。您不能对它们使用 Destroy/Free/FreeAndNil。那么如何清理 dynarray 呢?
好吧,你可能什么都不做,因为 dynarrays 是自动引用计数类型之一,如字符串和接口等。只要你的过程退出,Delphi 就会自动从局部变量中释放内存。
但是,如果你想在过程中间清理一个 dynarray,你可以通过 SetLength( MyDynArray, 0 )
或快捷方式 MyDynArray := nil
来完成
BlockRead(myFile, byteArray, sizeof(myFile), count)
误用 SizeOf
是错误的。但它还有另一个缺点:ByteArray
变量基本上是一个指针,所以它只有 4(四!)个字节(在 Win64 代码中是 8 个字节),所以你只需覆盖所有调用堆栈。你真的应该更好地使用现代类型安全 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 - 结果是实际读取了多少字节。因此,不是用文件内容填充数组,而是用 "how many bytes were read in one attempt?" 代替。那么你最好使用 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;
也就是所谓的"one-off error".
虽然由于历史原因,字符串的索引从 1 到 Length(s)
,并且通常没有第 0 个元素,但 dynarrays 不是。
动态数组的索引从 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;
关于动态数组的更多信息:
我已经尝试将文件读入 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;
文件很大吗?它可以一次放入 RAM 中吗?
您基本上有两个简单的选项可以从文件中创建 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/
- Why do the System.IOUtils functions and TStreamReader use 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;
相反,要将数组的内容保存到文件中,您可以使用 How to convert TBytes to Binary File? (using MemoryStream)
关于您的尝试。
http://wiki.freepascal.org/File_Handling_In_Pascal
如前所述,
SizeOf
与文件无关,即File
变量类型的内存大小。如果你想坚持使用旧的 TurboPascal API,那么你必须立即使用FileSize
函数来设置大小。对于小文件,它可以正常工作,对于大文件,这种方法 "read it all to memory at once, then process" 是错误的。inc(k); SetLength(ar,k);
- 在 +1 循环中 - 这是一个非常糟糕的主意,这意味着堆碎片和复制、重新复制和重新复制 gorwing 数据缓冲时间然后再次。那是Length*Length/2
缩放,也可能严重破坏堆内存结构(google 大约heap fragmentation
)。
如果可以 - 您需要检查之前的FileSize
并将数组设置为FreeAndNil(byteArray);
- 完全错误。数组不是对象。您不能对它们使用 Destroy/Free/FreeAndNil。那么如何清理 dynarray 呢? 好吧,你可能什么都不做,因为 dynarrays 是自动引用计数类型之一,如字符串和接口等。只要你的过程退出,Delphi 就会自动从局部变量中释放内存。 但是,如果你想在过程中间清理一个 dynarray,你可以通过SetLength( MyDynArray, 0 )
或快捷方式MyDynArray := nil
来完成
BlockRead(myFile, byteArray, sizeof(myFile), count)
误用SizeOf
是错误的。但它还有另一个缺点:ByteArray
变量基本上是一个指针,所以它只有 4(四!)个字节(在 Win64 代码中是 8 个字节),所以你只需覆盖所有调用堆栈。你真的应该更好地使用现代类型安全 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 - 结果是实际读取了多少字节。因此,不是用文件内容填充数组,而是用 "how many bytes were read in one attempt?" 代替。那么你最好使用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;
也就是所谓的"one-off error".
虽然由于历史原因,字符串的索引从 1 到 Length(s)
,并且通常没有第 0 个元素,但 dynarrays 不是。
动态数组的索引从 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;
关于动态数组的更多信息: