Leia arquivo binário em um struct

votos
42

Eu estou tentando ler dados binários usando C #. Eu tenho toda a informação sobre o layout dos dados nos arquivos que deseja ler. Eu sou capaz de ler o pedaço por pedaço de dados, ou seja, recebendo os primeiros 40 bytes de dados convertendo-a em uma corda, obter os próximos 40 bytes.

Uma vez que existem pelo menos três versão ligeiramente diferente dos dados, eu gostaria de ler os dados diretamente em um struct. Ele só se sente muito mais direito do que ao lê-lo linha por linha.

Eu tentei a seguinte abordagem, mas sem sucesso:

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();

O fluxo é um FileStream aberto a partir do qual eu começou a ler. Eu recebo um AccessViolationException quando se usa Marshal.PtrToStructure.

O fluxo contém mais informações do que eu estou tentando ler desde que eu não estou interessado em dados no final do arquivo.

A estrutura é definida como:

[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;
}

Os exemplos de código é alterado de original para fazer esta pergunta mais curto.

Como eu poderia ler dados binários de um arquivo em um struct?

Publicado 05/08/2008 em 15:28
fonte usuário
Em outras línguas...                            


7 respostas

votos
19

O problema é a seqüência de s em sua estrutura. Descobri que o empacotamento tipos como byte / short / int não é um problema; mas quando você precisa para organizar em um tipo complexo, como uma string, você precisa de seu struct para imitar explicitamente um tipo não gerenciado. Você pode fazer isso com o attrib MarshalAs.

Para o seu exemplo, o seguinte deve funcionar:

[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;
}
Respondeu 21/08/2008 em 20:02
fonte usuário

votos
8

Aqui está o que eu estou usando.
Isso funcionou com sucesso para mim para leitura portátil formato executável.
É uma função genérica, por isso Té o seu structtipo.

public static T ByteToType<T>(BinaryReader reader)
{
    byte[] bytes = reader.ReadBytes(Marshal.SizeOf(typeof(T)));

    GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    T theStructure = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
    handle.Free();

    return theStructure;
}
Respondeu 02/11/2010 em 03:40
fonte usuário

votos
5

Como disse Ronnie, eu usaria BinaryReader e ler cada campo individualmente. Não consigo encontrar o link para o artigo com esta informação, mas tem sido observado que o uso de BinaryReader para ler cada campo individual pode ser mais rápido do que Marshal.PtrToStruct, se a estrutura contém menos de 30-40 ou mais campos. Vou postar o link para o artigo quando eu encontrá-lo.

Link do artigo está em: http://www.codeproject.com/Articles/10750/Fast-Binary-File-Reading-with-C

Quando o empacotamento de uma matriz de estruturas, PtrToStruct ganha a mão superior mais rapidamente, porque você pode pensar a contagem de campo como campos * comprimento da matriz.

Respondeu 06/05/2010 em 02:04
fonte usuário

votos
3

Eu não tive sorte usando o BinaryFormatter, eu acho que tem que ter uma estrutura completa que corresponda ao conteúdo do arquivo exatamente. Percebi que no final eu não estava interessado em muito do conteúdo do arquivo de qualquer maneira, então eu fui com a solução de leitura de parte do fluxo em um ByteBuffer e depois convertê-lo usando

Encoding.ASCII.GetString()

para cordas e

BitConverter.ToInt32()

para os números inteiros.

I terá de ser capaz de analisar mais do arquivo mais tarde, mas para esta versão eu fui embora com apenas um par de linhas de código.

Respondeu 06/08/2008 em 10:03
fonte usuário

votos
1

Não vejo qualquer problema com o seu código.

apenas fora de minha cabeça, o que se você tentar fazê-lo manualmente? funciona?

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));
...
...
...

tente também

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();

em seguida, usar buffer [] em sua BinaryReader em vez de ler dados de FileStream para ver se você ainda receber exceção AccessViolation.

Eu não tive sorte usando o BinaryFormatter, eu acho que tem que ter uma estrutura completa que corresponda ao conteúdo do arquivo exatamente.

Isso faz sentido, BinaryFormatter tem o seu próprio formato de dados, totalmente incompatível com o seu.

Respondeu 05/08/2008 em 16:31
fonte usuário

votos
0

Leitura em linha reta em estruturas é mau - muitos um programa C caiu ao longo por causa de diferentes ordenamentos de byte, diferentes implementações do compilador de campos, embalagem, tamanho da palavra .......

Você está melhor de serializadas e deserialising byte por byte. Use a configuração na coisa se você quiser ou apenas se acostumar com BinaryReader.

Respondeu 23/09/2008 em 22:43
fonte usuário

votos
0

Tente isto:

using (FileStream stream = new FileStream(fileName, FileMode.Open))
{
    BinaryFormatter formatter = new BinaryFormatter();
    StructType aStruct = (StructType)formatter.Deserialize(filestream);
}
Respondeu 05/08/2008 em 15:56
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more