Pergunta

Estou tentando ler dados binários usando C#.Tenho todas as informações sobre o layout dos dados nos arquivos que desejo ler.Consigo ler os dados "pedaço por pedaço", ou seja,obtendo os primeiros 40 bytes de dados convertendo-os em uma string, obtenha os próximos 40 bytes.

Como existem pelo menos três versões ligeiramente diferentes dos dados, gostaria de ler os dados diretamente em uma estrutura.Parece muito mais certo do que ler "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 stream é um FileStream aberto do qual comecei a ler.eu recebo um AccessViolationException ao usar Marshal.PtrToStructure.

O fluxo contém mais informações do que estou tentando ler, pois não estou interessado nos 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;
}

O código de exemplos foi alterado do original para tornar esta questão mais curta.

Como eu leria dados binários de um arquivo em uma estrutura?

Nenhuma solução correta

Outras dicas

O problema é o cordaestá na sua estrutura.Descobri que empacotar tipos como byte/short/int não é um problema;mas quando você precisa empacotar em um tipo complexo, como uma string, você precisa que sua estrutura imite explicitamente um tipo não gerenciado.Você pode fazer isso com o atributo 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;
}

Aqui está o que estou usando.
Isso funcionou com sucesso para mim ao ler o Portable Executable Format.
É uma função genérica, então T é seu struct tipo.

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

Como Ronnie disse, eu usaria o BinaryReader e leria cada campo individualmente.Não consigo encontrar o link para o artigo com essas informações, mas foi observado que usar BinaryReader para ler cada campo individual pode ser mais rápido que Marshal.PtrToStruct, se a estrutura contiver menos de 30 a 40 campos.Postarei o link do artigo quando o encontrar.

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

Ao empacotar uma matriz de estruturas, PtrToStruct ganha vantagem mais rapidamente, porque você pode pensar na contagem de campos como campos * comprimento da matriz.

Não tive sorte ao usar o BinaryFormatter, acho que preciso ter uma estrutura completa que corresponda exatamente ao conteúdo do arquivo.Percebi que, no final das contas, não estava interessado muito no conteúdo do arquivo, então optei pela solução de ler parte do stream em um bytebuffer e depois convertê-lo usando

Encoding.ASCII.GetString()

para cordas e

BitConverter.ToInt32()

para os inteiros.

Precisarei analisar mais arquivos posteriormente, mas para esta versão consegui apenas algumas linhas de código.

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

acabou de sair da minha cabeça, e se você tentar fazer isso 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();

então use amortecedor[] no seu BinaryReader em vez de ler dados do FileStream para ver se você ainda obtém a exceção AccessViolation.

Não tive sorte usando o BinaryFormatter, acho que tenho que ter uma estrutura completa que corresponda exatamente ao conteúdo do arquivo.

Faz sentido, BinaryFormatter possui um formato de dados próprio, totalmente incompatível com o seu.

Experimente isto:

using (FileStream stream = new FileStream(fileName, FileMode.Open))
{
    BinaryFormatter formatter = new BinaryFormatter();
    StructType aStruct = (StructType)formatter.Deserialize(filestream);
}

Ler diretamente nas estruturas é ruim - muitos programas C caíram devido a diferentes ordenações de bytes, diferentes implementações de campos do compilador, empacotamento, tamanho do Word.......

Você é o melhor em serializar e desserializar byte por byte.Use o material incorporado se quiser ou apenas se acostume com o BinaryReader.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top