Question

My win32 program created a binary file with only a string[32] and an integer right after it. Then, I wrote a .NET program to read that same file.

Here is my .NET code:

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;

It failed to read the file probably. In fact, it raised "read beyond end of file" exception. The reason for that is because the string is exactly 32 characters long. I believe that the BinaryReader doesn't have the information. So, it reads beyond 32 characters for the string. Thus, it fails to read the binary file properly.

So, how do you read a binary-win32-file under .NET framework in this case?

UPDATE

Here is my .NET updated code:

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;

Although this works, I can't seem to retrieve the string from tmparray. FHeader.id is a string type. ToString doesn't seem to be working right. After that line of code, FHeader.id is equals "System.Char[]." It doesn't actually contain the string itself.

Any idea?

Thanks in advance,

Était-ce utile?

La solution

You stored a Delphi ShortString into the file. A ShortString contains a Byte at the beginning to specify how many AnsiChar elements are in the ShortString. In your .NET code, you need to read a Byte, then read the specified number of 8-bit characters, then read a 4-byte integer, eg:

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;

Autres conseils

As explained in the documentation of ReadString, it expects the string to be "prefixed with the length, encoded as an integer seven bits at a time." (that's a little unclear, but I guess most people would be reading strings they wrote using BinaryWriter.Write(String)).

If you have a string of known length (such as 32 in this case) or want to read the entire file, you should probably use one of the BinaryReader.Read overloads

Answer for updated question:

char[].ToString() will not concatenate the characters into a string. Instead, it will give a descriptive representation of an array of chars ("System.Char[]").

What you can do is use the string constructor to convert the char[] to an equivalent string. See this answer.

Update: As the other answer and the comments mention, you should take note of the correct encoding when converting the char[] to a string. The String(Char[]) constructor assumes unicode characters, which may or may not be what you need (it will work for plain ASCII though)

BinaryReader.ReadString() can only read strings that were written by BinaryReader.WriteString(). The string data in the file is pre-fixed with a variable length field that stores the string length.

The workaround is simple, you just need to call ReadBytes(32) instead. Then convert the bytes to a string using Encoding.GetString().

What's not so simple is selecting the proper Encoding class. It needs to match the encoding that was used in the program that wrote the file. Which is an ugly implementation detail that can get you in trouble with files that were written in another part of the world. Encoding.Default will work when the file didn't travel very far.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top