Domanda

Sto cercando di leggere i dati binari utilizzando C#.Ho tutte le informazioni circa il layout dei dati nei file ho voglia di leggere.Io sono in grado di leggere i dati "blocco per blocco", cioèil primo 40 byte di dati conversione di una stringa, il 40 byte.

Dato che ci sono almeno tre versione leggermente diversa di dati, vorrei leggere i dati direttamente in una struct.Si sente molto di più che leggendo "riga per riga".

Ho provato il seguente approccio, ma senza alcun risultato:

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

Il flusso è aperto FileStream da che ho cominciato a leggere.Ho un AccessViolationException quando si utilizza Marshal.PtrToStructure.

Il flusso contiene più informazioni di quanto sto cercando di leggere in quanto non mi interessano i dati alla fine del file.

La struttura è definita come:

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

Gli esempi di codice è cambiato dall'originale per fare questa domanda più breve.

Come faccio a leggere i dati binari da un file in una struct?

Nessuna soluzione corretta

Altri suggerimenti

Il problema è il strings nella vostra struttura.Ho trovato che il marshalling di tipi come byte/short/int non è un problema;ma quando si ha bisogno di un maresciallo in un complesso di tipo, ad esempio una stringa, è necessario disporre di una struttura in modo esplicito imitare un tipo non gestito.Si può fare questo con il MarshalAs attrib.

Per il tuo esempio, il seguente dovrebbe funzionare:

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

Qui è quello che sto usando.
Questo ha funzionato con successo per me per la lettura Portatile in Formato Eseguibile.
È una funzione generica, in modo T è il tuo 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;
}

Come Ronnie ha detto, mi piacerebbe usare il BinaryReader e leggere singolarmente ogni campo.Non riesco a trovare il link per l'articolo con questa info, ma è stato osservato che l'utilizzo di BinaryReader per leggere ogni singolo campo può essere più veloce di Maresciallo.PtrToStruct, se la struttura contiene meno di 30-40 o in modo che i campi.Vi posto il link per l'articolo quando la trovo.

Il link di un articolo, è: http://www.codeproject.com/Articles/10750/Fast-Binary-File-Reading-with-C

Quando il marshalling di un array di struct, PtrToStruct guadagni superiori a mano più rapidamente, perché non si può pensare di campo conte come campi * lunghezza dell'array.

Ho avuto la fortuna utilizzando il BinaryFormatter, credo di avere una completa struttura che corrisponde al contenuto del file esattamente.Ho capito che alla fine non mi interessava molto il contenuto del file, comunque, così sono andato con la soluzione di lettura di parte del flusso in un bytebuffer e poi convertirlo utilizzando

Encoding.ASCII.GetString()

per le stringhe e

BitConverter.ToInt32()

per i numeri interi.

Ho bisogno di essere in grado di analizzare più di un file più avanti, ma per questa versione me la sono cavata con solo un paio di righe di codice.

Io non vedo alcun problema con il tuo codice.

appena fuori dalla mia testa, cosa succede se si tenta di farlo manualmente?funziona?

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

anche provare a

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

quindi utilizzare buffer[] nel BinaryReader invece di leggere i dati da FileStream per vedere se è ancora ottenere AccessViolation eccezione.

Ho avuto la fortuna utilizzando il BinaryFormatter, mi sa che devo avere una completa struttura che corrisponde il contenuto del file esattamente.

Che abbia un senso, BinaryFormatter ha un formato di dati propri, del tutto incompatibile con la vostra.

Prova questo:

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

La lettura direttamente in strutture è male, per molti un programma in C è caduto a causa di diversi byte ordinamenti, compilatore diverse implementazioni dei campi, l'imballaggio, la dimensione di parola.......

Voi sono migliori di serialising e deserialising byte per byte.Utilizzare la costruzione di roba se si desidera o si abitua a BinaryReader.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top