Lees binair bestand in een structuur

stemmen
42

Ik ben op zoek naar binaire gegevens lezen met behulp van C #. Ik heb alle informatie over de indeling van de gegevens in de bestanden die ik wil lezen. Ik ben in staat om de gegevens brok door brok ​​te lezen, dat wil zeggen het verkrijgen van de eerste 40 bytes van de gegevens te converteren naar een string, krijgen de komende 40 bytes.

Aangezien er ten minste drie iets andere versie van de gegevens, zou ik graag lezen van de gegevens direct in een structuur. Het voelt gewoon zo veel meer recht dan door lijn per lijn te lezen.

Ik heb geprobeerd, maar de volgende aanpak tevergeefs:

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

De stroom is een geopende FileStream waaruit ik begon te lezen uit. Ik krijg een AccessViolationException bij het gebruik Marshal.PtrToStructure.

De stroom bevat meer informatie dan ik probeer te lezen, want ik ben niet geïnteresseerd in gegevens aan het einde van het bestand.

De structuur wordt gedefinieerd als:

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

De voorbeelden code wordt gewijzigd van de originele op deze vraag in te korten.

Hoe zou ik gelezen dat binaire gegevens van een bestand in een structuur?

De vraag is gesteld op 05/08/2008 om 15:28
bron van user
In andere talen...                            


7 antwoorden

stemmen
19

Het probleem is de snaar s in uw structuur. Ik vond dat marshaling types zoals byte / short / int is geen probleem; maar als je moet bundelen in een complex type, zoals een string, je structuur moet u expliciet na te bootsen een onbeheerde type. U kunt dit doen met de MarshalAs attrib.

Voor uw moet bijvoorbeeld de volgende werkzaamheden:

[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;
}
antwoordde op 21/08/2008 om 20:02
bron van user

stemmen
8

Hier is wat ik gebruik.
Dit werkte met succes voor mij voor het lezen van Portable Executable Format.
Het is een generieke functie, dus Tis uw 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;
}
antwoordde op 02/11/2010 om 03:40
bron van user

stemmen
5

Zoals Ronnie gezegd, zou ik BinaryReader gebruiken en te lezen elk veld afzonderlijk. Ik kan het niet vinden van de link naar het artikel met deze info, maar het is waargenomen dat het gebruik van BinaryReader aan elk afzonderlijk veld te lezen sneller dan Marshal.PtrToStruct kunnen zijn, als de structuur bevat minder dan 30-40 of zo velden. Ik kom na de link naar het artikel toen ik vind het.

Koppeling van het artikel is op: http://www.codeproject.com/Articles/10750/Fast-Binary-File-Reading-with-C

Wanneer marshaling een array van structs, PtrToStruct krijgt de overhand sneller, want je kunt bedenken van het veld tellen als velden * arraylengte.

antwoordde op 06/05/2010 om 02:04
bron van user

stemmen
3

Ik had geen geluk met de BinaryFormatter, ik denk dat ik moet een complete structuur die overeenkomt met de inhoud van het bestand precies hebben. Ik realiseerde me dat op het einde was ik niet geïnteresseerd in een zeer groot deel van de inhoud van het bestand toch dus ik ging met de oplossing van een deel van stroom te lezen in een bytebuffer en vervolgens omzetten met behulp van

Encoding.ASCII.GetString()

voor strijkers en

BitConverter.ToInt32()

voor de gehele getallen.

Ik moet in staat zijn om meer van het bestand ontleden later, maar voor deze versie heb ik weg met slechts een paar regels code.

antwoordde op 06/08/2008 om 10:03
bron van user

stemmen
1

Ik heb geen probleem met uw code te zien.

gewoon uit mijn hoofd, wat als je deze handmatig proberen te doen? werkt het?

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

probeer ook

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

dan gebruik maken van buffer [] in uw BinaryReader in plaats van het lezen van gegevens van FileStream om te zien of je nog steeds AccessViolation uitzondering.

Ik had geen geluk met de BinaryFormatter, ik denk dat ik moet een complete structuur die overeenkomt met de inhoud van het bestand precies hebben.

Dat klinkt logisch, BinaryFormatter heeft zijn eigen dataformaat, volstrekt onverenigbaar met de jouwe.

antwoordde op 05/08/2008 om 16:31
bron van user

stemmen
0

Het lezen van recht in de structuren is kwaad - menig C-programma is gedaald als gevolg van verschillende byte ordeningen, andere compiler implementaties van velden, de verpakking, de grootte van het woord .......

Je bent best of serialising en deserialising byte voor byte. Gebruik de ingebouwde spullen als je wilt of gewoon wennen aan BinaryReader.

antwoordde op 23/09/2008 om 22:43
bron van user

stemmen
0

Probeer dit:

using (FileStream stream = new FileStream(fileName, FileMode.Open))
{
    BinaryFormatter formatter = new BinaryFormatter();
    StructType aStruct = (StructType)formatter.Deserialize(filestream);
}
antwoordde op 05/08/2008 om 15:56
bron van user

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