Încerc să citesc datele binare folosind C #. Am toate informațiile despre aspectul datelor din fișierele pe care vreau să le citesc. Sunt capabil să citesc datele "bucată pe bucată", adică să primesc primele 40 octeți de date care o convertesc într-un șir, să obțin următoarele 40 de octeți.
Întrucât există cel puțin trei versiuni ușor diferite ale datelor, aș dori să citesc datele direct într-un struct. Simt doar mult mai mult decât căutând-o "pe linie".
Am încercat următoarea abordare, dar fără nici un folos:
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();
Fluxul este un FileStream deschis de la care am început să citesc. Obțineți un AccessViolationExceptio
n când utilizați Marshal.PtrToStructure
.
Fluxul conține mai multe informații decât încerc să citesc deoarece nu mă interesează datele de la sfârșitul fișierului.
Structura este definită ca:
[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;
}
Codul de exemple este modificat de la original pentru a face această întrebare mai scurtă.
Cum aș citi datele binare dintr-un fișier într-un struct?
Nu văd nici o problemă cu codul tău.
doar din cap, dar dacă încerci să o faci manual? funcționează?
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));
...
...
...
încercați, de asemenea
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();
apoi utilizați tampon [] în BinaryReader în loc să citiți date din FileStream pentru a vedea dacă totuși obțineți excepția AccessViolation.
Nu am avut noroc folosind BinaryFormatter, cred că trebuie aveți un struct complet care se potrivește exact conținutul fișierului.
Asta are sens, BinaryFormatter are propriul format de date, complet incompatibil cu a ta.
Nu am avut noroc folosind BinaryFormatter, cred că trebuie să am o structură completă care să se potrivească exact cu conținutul fișierului. Mi-am dat seama că, în cele din urmă, nu m-am interesat prea mult de conținutul fișierului, așa că am mers cu soluția de a citi o parte a fluxului într-un bytebuffer și apoi să îl convertesc folosind
Encoding.ASCII.GetString()
pentru șiruri de caractere și
BitConverter.ToInt32()
pentru numere întregi.
Va trebui să fiu capabil să analizez mai mult fișierul mai târziu, dar pentru această versiune am scăpat cu doar câteva linii de cod.
Problema este șirul s din structura dvs. Am constatat că tipurile de marshaling cum ar fi byte / short / int nu reprezintă o problemă; dar atunci când aveți nevoie să marshal într-un tip complex, cum ar fi un șir, aveți nevoie de struct dumneavoastră pentru a imita în mod explicit un tip neangajat. Poți să faci asta cu atributul Marshal.
Pentru exemplul dvs., ar trebui să funcționeze următoarele:
[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;
}
Citirea direct în structuri este rău - multe dintre programele C au căzut din cauza ordinelor diferite de octeți, implementări diferite de compilatoare ale câmpurilor, ambalare, dimensiune de cuvânt .......
Ești cel mai bun de a serializa și de a descoperi byte prin byte. Utilizați build-ul în chestii dacă doriți sau pur și simplu să vă obișnuiți cu BinaryReader.
După cum a spus Ronnie, aș folosi BinaryReader și citesc fiecare câmp individual. Nu găsesc linkul la articol cu această informație, dar sa observat că folosirea lui BinaryReader pentru citirea fiecărui câmp individual poate fi mai rapid decât Marshal.PtrToStruct, dacă structul conține mai puțin de 30-40 de câmpuri. Voi posta linkul la articol când îl găsesc.
The article's link is at: http://www.codeproject.com/Articles/10750/Fast-Binary-File-Reading-with-C
Atunci când marshaling o serie de structuri, PtrToStruct câștigă mai repede de mâna superioară, pentru că vă puteți gândi la numărul de câmp ca câmp * lungime matrice.
Iată ce folosesc.
Acest lucru a funcționat cu succes pentru citirea formatului portabil executabil.
Este o funcție generică, deci T
este struct
.
public static T ByteToType(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;
}