如何从使用 .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 个字符?的主要内容,如果未能解决你的问题,请参考以下文章

.NET下如何拦截鼠标、键盘消息?Win32NET来帮你

.NET下如何拦截鼠标键盘消息?Win32NET来帮你

如何增加 ASP.NET Core 二进制文件(32 位)的堆栈大小?

如何使用 Visual Studio 2019 构建最兼容的二进制文件?

如何从像素缓冲区创建 Win32 HBITMAP

sqlite3 install 和使用