如何从使用 .NET 在 win32 下创建的二进制文件中读取 n 个字符?
Posted
技术标签:
【中文标题】如何从使用 .NET 在 win32 下创建的二进制文件中读取 n 个字符?【英文标题】:How to read n characters from a binary file created under win32 with .NET? 【发布时间】:2012-05-08 06:04:13 【问题描述】:我的 win32 程序创建了一个二进制文件,其后只有一个字符串 [32] 和一个整数。然后,我编写了一个 .NET 程序来读取同一个文件。
这是我的 .NET 代码:
method ReadUnitFile;
var
FHeader:TFileHeader;
Biread:BinaryReader;
FUnitLoc:String;
begin
FUnitLoc := baseDir+'\system\Units.dat';
if Environment.OSVersion.Platform = System.PlatformID.Unix then
FUnitLoc := baseDir+'/system/Units.dat';
if File.Exists(FUnitLoc) then
begin
Biread:= new BinaryReader(File.OpenRead(FUnitLoc));
FHeader.id:=Biread.ReadString;
FHeader.version:=Biread.ReadInt32;
Biread.Close;
end;
end;
可能是读取文件失败。实际上,它引发了“读取超出文件末尾”异常。原因是字符串长度正好是 32 个字符。我相信 BinaryReader 没有这些信息。因此,它读取的字符串超过 32 个字符。因此,它无法正确读取二进制文件。
那么,在这种情况下,如何在 .NET 框架下读取 binary-win32 文件?
更新
这是我的 .NET 更新代码:
method ReadUnitFile;
var
FHeader:TFileHeader;
Biread:BinaryReader;
FUnitLoc:String;
tmparray:array[0..32] of char;
begin
FUnitLoc := baseDir+'\system\Units.dat';
if Environment.OSVersion.Platform = System.PlatformID.Unix then
FUnitLoc := baseDir+'/system/Units.dat';
if File.Exists(FUnitLoc) then
begin
Biread:= new BinaryReader(File.OpenRead(FUnitLoc));
Biread.Read(tmparray,0,32);
FHeader.id := tmparray.ToString;
FHeader.version:=Biread.ReadInt32;
Biread.Close;
end;
end;
虽然这可行,但我似乎无法从 tmparray 中检索字符串。 FHeader.id 是字符串类型。 ToString 似乎无法正常工作。在这行代码之后,FHeader.id 等于“System.Char[]”。它实际上并不包含字符串本身。
有什么想法吗?
提前致谢,
【问题讨论】:
【参考方案1】:您将 Delphi ShortString
存储到文件中。一个ShortString
在开头包含一个Byte
,用于指定ShortString
中有多少个AnsiChar
元素。在你的.NET代码中,你需要读取一个Byte
,然后读取指定数量的8位字符,然后读取一个4字节整数,例如:
method ReadUnitFile;
var
FHeader: TFileHeader;
Biread: BinaryReader;
FUnitLoc: String;
begin
FUnitLoc := baseDir+'\system\Units.dat';
if Environment.OSVersion.Platform = System.PlatformID.Unix then
FUnitLoc := baseDir+'/system/Units.dat';
if File.Exists(FUnitLoc) then
begin
Biread := new BinaryReader(File.OpenRead(FUnitLoc));
FHeader.id := System.Encoding.Default.GetString(Biread.ReadBytes(Biread.ReadByte));
FHeader.version := Biread.ReadInt32;
Biread.Close;
end;
end;
【讨论】:
【参考方案2】:正如documentation of ReadString 中所解释的,它期望字符串“以长度为前缀,一次编码为一个整数七位”。 (这有点不清楚,但我猜大多数人会阅读他们使用BinaryWriter.Write(String)
编写的字符串。
如果您有一个已知长度的字符串(例如本例中的 32)或想要读取整个文件,您可能应该使用 BinaryReader.Read
重载之一
更新问题的答案:
char[].ToString()
不会将字符连接成字符串。相反,它将给出一个字符数组的描述性表示 ("System.Char[]"
)。
您可以使用string
构造函数将char[]
转换为等效字符串。见this answer。
更新:正如另一个答案和 cmets 提到的,在将 char[]
转换为 string
时,您应该注意正确的编码。 String(Char[])
构造函数 assumes unicode characters,它可能是也可能不是你需要的(虽然它适用于纯 ASCII)
【讨论】:
对此进行扩展,一旦读取,您可以使用System.Text.Encoding.ASCII.GetString(tmparray)
将 ASCII 字节数组转换为字符串。如果您对此感到满意,我可以将其添加到您的答案中?
Deanna - new String(tmparray)
有什么问题? OP 正在使用char[]
习惯,我没有注意到它是一个字符数组。我倾向于使用字节数组和 ASCII,我假设它们的文件数据是 ASCII,但是正确设置阅读器编码的 char 数组也可以。【参考方案3】:
BinaryReader.ReadString() 只能读取由 BinaryReader.WriteString() 写入的字符串。文件中的字符串数据以一个可变长度字段为前缀,该字段存储字符串长度。
解决方法很简单,您只需调用 ReadBytes(32) 即可。然后使用 Encoding.GetString() 将字节转换为字符串。
选择正确的编码类并不那么简单。它需要与编写文件的程序中使用的编码相匹配。这是一个丑陋的实现细节,可能会让您在处理在世界其他地方编写的文件时遇到麻烦。 Encoding.Default 将在文件没有传播很远时起作用。
【讨论】:
以上是关于如何从使用 .NET 在 win32 下创建的二进制文件中读取 n 个字符?的主要内容,如果未能解决你的问题,请参考以下文章
如何增加 ASP.NET Core 二进制文件(32 位)的堆栈大小?