Les binærfil i en struct

stemmer
44

Jeg prøver å lese binære data ved hjelp av C #. Jeg har all informasjon om utformingen av dataene i filene jeg ønsker å lese. Jeg er i stand til å lese data del av klump, dvs. å få de første 40 byte data konvertere den til en streng, får de neste 40 bytes.

Siden det er minst tre litt annen versjon av dataene, vil jeg gjerne lese data direkte inn i en struct. Det føles bare så mye mer rett enn ved å lese det linje for linje.

Jeg har prøvd følgende tilnærming, men til ingen nytte:

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

Strømmen er en åpnet Filestream som jeg har begynte å lese fra. Jeg får en AccessViolationException ved bruk Marshal.PtrToStructure.

Strømmen inneholder mer informasjon enn jeg prøver å lese siden jeg ikke er interessert i data på slutten av filen.

Struct er definert som:

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

Den eksempler koden er endret fra opprinnelig å gjøre dette spørsmålet kortere.

Hvordan skulle jeg lese binære data fra en fil til en struct?

Publisert på 05/08/2008 klokken 14:28
kilden bruker
På andre språk...                            


7 svar

stemmer
22

Problemet er streng s i struct. Jeg fant ut at marshaling typer som byte / kort / int er ikke et problem; men når du trenger å skaffe til veie inn i en kompleks type, for eksempel en streng, må du din struct eksplisitt etterligne en uovervåkede type. Du kan gjøre dette med MarshalAs attrib.

For eksempel bør følgende arbeide:

[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;
}
Svarte 21/08/2008 kl. 19:02
kilden bruker

stemmer
8

Her er hva jeg bruker.
Dette fungerte vellykket for meg for å lese Portable Executable Format.
Det er en generisk funksjon, så Ter din structtype.

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;
}
Svarte 02/11/2010 kl. 02:40
kilden bruker

stemmer
5

Som Ronnie sagt, vil jeg bruke BinaryReader og lese hvert felt individuelt. Jeg kan ikke finne link til artikkelen med denne informasjonen, men det er blitt observert at bruk BinaryReader å lese hvert enkelt felt kan være raskere enn Marshal.PtrToStruct, hvis struct inneholder mindre enn 30-40 eller så felt. Jeg skal legge inn link til artikkelen når jeg finner den.

Artikkelen link er på: http://www.codeproject.com/Articles/10750/Fast-Binary-File-Reading-with-C

Når marshaling en rekke structs, får PtrToStruct den øverste hånden raskere, fordi du kan tenke på feltet teller som felt * matrise lengde.

Svarte 06/05/2010 kl. 01:04
kilden bruker

stemmer
3

Jeg hadde ikke flaks med BinaryFormatter, jeg tror jeg må ha en komplett struct som samsvarer med innholdet av filen nøyaktig. Jeg innså at til slutt var jeg ikke interessert i veldig mye av filinnholdet uansett så jeg gikk med løsningen av å lese en del av strømmen i en bytebuffer og deretter konvertere den ved hjelp

Encoding.ASCII.GetString()

for strykere og

BitConverter.ToInt32()

for heltall.

Jeg må være i stand til å analysere mer av filen senere, men for denne versjonen jeg slapp unna med bare et par linjer med kode.

Svarte 06/08/2008 kl. 09:03
kilden bruker

stemmer
1

Jeg ser ikke noe problem med koden din.

bare ut av hodet mitt, hva om du prøver å gjøre det manuelt? virker det?

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

også prøve

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

deretter bruke buffer [] i BinaryReader stedet for å lese data fra Filestream for å se om du fortsatt får AccessViolation unntak.

Jeg hadde ikke flaks med BinaryFormatter, jeg tror jeg må ha en komplett struct som samsvarer med innholdet av filen nøyaktig.

Det er fornuftig, BinaryFormatter har sin egen dataformat, helt uforenlig med ditt.

Svarte 05/08/2008 kl. 15:31
kilden bruker

stemmer
0

Reading rett inn structs er ond - mange et C-program har falt på grunn av forskjellige byte orde, ulike kompilatoren implementeringer av felt, pakking, ordet størrelse .......

Du er best av serialising og deserialising byte av byte. Bruk bygge i ting hvis du vil, eller bare bli vant til BinaryReader.

Svarte 23/09/2008 kl. 21:43
kilden bruker

stemmer
0

Prøv dette:

using (FileStream stream = new FileStream(fileName, FileMode.Open))
{
    BinaryFormatter formatter = new BinaryFormatter();
    StructType aStruct = (StructType)formatter.Deserialize(filestream);
}
Svarte 05/08/2008 kl. 14:56
kilden bruker

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