我正在尝试使用C#读取二进制数据。我有关于我想要阅读的文件中数据布局的所有信息。我能够读取数据“chunk by chunk”,即获取前40个字节的数据将其转换为字符串,获得接下来的40个字节。
由于至少有三种略有不同的数据版本,我想将数据直接读入结构中。它只是通过“逐行”阅读它而感觉更加正确。
我尝试了以下方法,但无济于事:
StructType aStruct;
int count = Marshal.SizeOf(typeof(StructType));
byte[] readBuffer = new byte[count];
BinaryReader reader = new BinaryReader(stream);
readBuffer = reader.ReadBytes(count);
GCHandle handle = GCHandle.Alloc(readBuffer, GCHandleType.Pinned);
aStruct = (StructType) Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(StructType));
handle.Free();
该流是一个打开的FileStream,我已经开始从中读取。使用AccessViolationExceptio
时,我得到Marshal.PtrToStructure
n。
该流包含的信息比我尝试阅读的信息多,因为我对文件末尾的数据不感兴趣。
结构定义如下:
[StructLayout(LayoutKind.Explicit)]
struct StructType
{
[FieldOffset(0)]
public string FileDate;
[FieldOffset(8)]
public string FileTime;
[FieldOffset(16)]
public int Id1;
[FieldOffset(20)]
public string Id2;
}
示例代码从原始代码更改为更短的问题。
如何将文件中的二进制数据读入结构?
答案 0 :(得分:25)
问题是结构中的字符串。我发现像byte / short / int这样的编组类型不是问题;但是当你需要编组成一个复杂的类型(如字符串)时,你需要你的结构显式地模仿非托管类型。您可以使用MarshalAs attrib。
执行此操作对于您的示例,以下内容应该有效:
[StructLayout(LayoutKind.Explicit)]
struct StructType
{
[FieldOffset(0)]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
public string FileDate;
[FieldOffset(8)]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
public string FileTime;
[FieldOffset(16)]
public int Id1;
[FieldOffset(20)]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 66)] //Or however long Id2 is.
public string Id2;
}
答案 1 :(得分:11)
这就是我正在使用的内容。
这对于我阅读可移植可执行文件格式成功。它是一个通用函数,因此 T
是您的{ {1}}输入。
struct
答案 2 :(得分:5)
正如罗尼所说,我会使用BinaryReader并单独阅读每个字段。我找不到带有此信息的文章的链接,但据观察,如果struct包含少于30-40个字段,使用BinaryReader读取每个单独的字段可能比Marshal.PtrToStruct更快。当我找到它时,我会发布文章的链接。
该文章的链接位于:http://www.codeproject.com/Articles/10750/Fast-Binary-File-Reading-with-C
当编组结构数组时,PtrToStruct可以更快地获得优势,因为您可以将字段数视为字段*数组长度。
答案 3 :(得分:3)
我没有运气使用BinaryFormatter,我想我必须有一个完全匹配文件内容的完整结构。我意识到最后我对大部分文件内容都不感兴趣,所以我选择了将部分流读入bytebuffer然后使用
进行转换的解决方案。Encoding.ASCII.GetString()
表示字符串和
BitConverter.ToInt32()
表示整数。
稍后我需要能够解析更多的文件,但对于这个版本,我只需要几行代码就可以了。
答案 4 :(得分:2)
我认为您的代码没有任何问题。
刚出头,如果你试图手动怎么办?它有用吗?
BinaryReader reader = new BinaryReader(stream);
StructType o = new StructType();
o.FileDate = Encoding.ASCII.GetString(reader.ReadBytes(8));
o.FileTime = Encoding.ASCII.GetString(reader.ReadBytes(8));
...
...
...
也试试
StructType o = new StructType();
byte[] buffer = new byte[Marshal.SizeOf(typeof(StructType))];
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
Marshal.StructureToPtr(o, handle.AddrOfPinnedObject(), false);
handle.Free();
然后在BinaryReader中使用 buffer [] ,而不是从FileStream中读取数据,看看你是否仍然获得了AccessViolation异常。
我没有运气使用 BinaryFormatter,我想我必须这样做 有一个匹配的完整结构 文件的内容完全正确。
这是有道理的,BinaryFormatter有自己的数据格式,与你的数据格式完全不兼容。
答案 5 :(得分:0)
试试这个:
using (FileStream stream = new FileStream(fileName, FileMode.Open))
{
BinaryFormatter formatter = new BinaryFormatter();
StructType aStruct = (StructType)formatter.Deserialize(filestream);
}
答案 6 :(得分:0)
直接阅读结构是邪恶的 - 许多C程序因为不同的字节顺序,字段的不同编译器实现,打包,字大小而失败.......
您最好逐字节串行化和反序列化。如果你愿意,可以使用build in stuff或者习惯使用BinaryReader。