c# - Binärdatei in eine Struktur einlesen

Translate

Ich versuche, Binärdaten mit C # zu lesen. Ich habe alle Informationen über das Layout der Daten in den Dateien, die ich lesen möchte. Ich bin in der Lage, die Daten "Stück für Stück" zu lesen, dh die ersten 40 Bytes der Daten, die in eine Zeichenfolge konvertiert werden, erhalten die nächsten 40 Bytes.

Da es mindestens drei leicht unterschiedliche Versionen der Daten gibt, möchte ich die Daten direkt in eine Struktur einlesen. Es fühlt sich einfach so viel besser an, als es "Zeile für Zeile" zu lesen.

Ich habe den folgenden Ansatz versucht, aber ohne Erfolg:

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

Der Stream ist ein geöffneter FileStream, aus dem ich zu lesen begonnen habe. Ich bekomme eineAccessViolationException bei VerwendungMarshal.PtrToStructure.

Der Stream enthält mehr Informationen als ich zu lesen versuche, da ich nicht an Daten am Ende der Datei interessiert bin.

Die Struktur ist wie folgt definiert:

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

Der Beispielcode wurde vom Original geändert, um diese Frage zu verkürzen.

Wie würde ich Binärdaten aus einer Datei in eine Struktur lesen?

This question and all comments follow the "Attribution Required."

Alle Antworten

Translate

Das Problem ist dasZeichenfolges in deiner Struktur. Ich fand, dass das Marshalling von Typen wie Byte / Short / Int kein Problem ist. Wenn Sie jedoch einen komplexen Typ wie einen String zusammenstellen müssen, muss Ihre Struktur einen nicht verwalteten Typ explizit nachahmen. Sie können dies mit dem Attribut MarshalAs tun.

Für Ihr Beispiel sollte Folgendes funktionieren:

[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;
}
Quelle
Bradley Lee
Translate

Hier ist was ich benutze.
Dies hat bei mir beim Lesen des Portable Executable Format erfolgreich funktioniert.
Es ist also eine generische FunktionTist deinstructArt.

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

Wie Ronnie sagte, würde ich BinaryReader verwenden und jedes Feld einzeln lesen. Ich kann den Link zu dem Artikel mit diesen Informationen nicht finden, aber es wurde festgestellt, dass die Verwendung von BinaryReader zum Lesen jedes einzelnen Felds schneller sein kann als Marshal.PtrToStruct, wenn die Struktur weniger als 30-40 Felder enthält. Ich werde den Link zum Artikel veröffentlichen, wenn ich ihn finde.

Der Link des Artikels lautet:http://www.codeproject.com/Articles/10750/Fast-Binary-File-Reading-with-C

Beim Marshalling eines Arrays von Strukturen gewinnt PtrToStruct schneller die Oberhand, da Sie sich die Feldanzahl als Felder * Arraylänge vorstellen können.

Quelle
Translate

Ich hatte kein Glück mit dem BinaryFormatter. Ich denke, ich muss eine vollständige Struktur haben, die genau zum Inhalt der Datei passt. Am Ende wurde mir klar, dass ich am Ende sowieso nicht sehr viel an dem Dateiinhalt interessiert war, also entschied ich mich, einen Teil des Streams in einen Bytebuffer zu lesen und ihn dann mit zu konvertieren

Encoding.ASCII.GetString()

für Saiten und

BitConverter.ToInt32()

für die ganzen Zahlen.

Ich muss später in der Lage sein, mehr von der Datei zu analysieren, aber für diese Version bin ich mit nur ein paar Codezeilen davongekommen.

Quelle
Translate

Ich sehe kein Problem mit Ihrem Code.

Was ist, wenn Sie versuchen, es manuell zu tun? funktioniert es?

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

Versuch auch

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

dann benutzePuffer[]in Ihrem BinaryReader, anstatt Daten aus FileStream zu lesen, um festzustellen, ob Sie weiterhin eine AccessViolation-Ausnahme erhalten.

Ich hatte kein Glück mit dem BinaryFormatter. Ich denke, ich muss eine vollständige Struktur haben, die genau zum Inhalt der Datei passt.

Das macht Sinn, BinaryFormatter hat ein eigenes Datenformat, das mit Ihrem völlig inkompatibel ist.

Quelle
Translate

Versuche dies:

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

Das direkte Lesen in Strukturen ist böse - so manches C-Programm ist aufgrund unterschiedlicher Bytereihenfolgen, unterschiedlicher Compiler-Implementierungen von Feldern, Packung, Wortgröße umgefallen .......

Sie können Byte für Byte am besten serialisieren und deserialisieren. Verwenden Sie das eingebaute Material, wenn Sie möchten oder sich nur an BinaryReader gewöhnen möchten.

Quelle