Číst binární soubor do struct

hlasů
42

Snažím se číst binární data pomocí C #. Mám všechny informace o rozložení dat v souborech chci přečíst. Jsem schopen číst data „kus od kusu“, tedy získávání prvních 40 bajtů dat převedením na provázku, získat dalších 40 bytů.

Vzhledem k tomu, existují nejméně tři poněkud odlišné verze dat, bych chtěl číst data přímo do struct. To mi prostě připadá mnohem větší nárok než čtení „řádek po řádku“.

Zkoušel jsem následující postup, ale marně:

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

Proud je otevřen FileStream, ze kterého jsem začal číst z. I získat AccessViolationException při použití Marshal.PtrToStructure.

Proud obsahuje více informací, než se snažím číst, protože nemám zájem dat na konci souboru.

Struct je definován jako:

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

Kód příklady se změnil z původní, aby se tato otázka kratší.

Jak bych mohl číst binární data ze souboru do struct?

Položena 05/08/2008 v 15:28
zdroj uživatelem
V jiných jazycích...                            


7 odpovědí

hlasů
19

Problémem je řetězec s ve vaší struct. Zjistil jsem, že zařazování typy jako byte / krátké / int není problém; ale když budete potřebovat k zařazování do komplexního typu, jako je například řetězec, budete potřebovat struct explicitně napodobit neřízený typu. To lze provést pomocí MarshalAs attrib.

Pro vaši Například následující by měl fungovat:

[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;
}
Odpovězeno 21/08/2008 v 20:02
zdroj uživatelem

hlasů
8

Zde je to, co jsem pomocí.
To úspěšně pracoval pro mě čtení Portable Executable Format.
Je to obecný funkce, takže Tje váš structtyp.

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;
}
Odpovězeno 02/11/2010 v 03:40
zdroj uživatelem

hlasů
5

Jak řekl Ronnie, bych použít BinaryReader a číst každé pole zvlášť. Nemohu najít odkaz na článek s touto informací, ale to bylo pozorováno, že použití BinaryReader číst jednotlivé pole může být rychlejší než Marshal.PtrToStruct, v případě, že struct obsahuje méně než 30-40 nebo tak pole. Budu post odkaz na článek, když jsem ho najít.

Link Tento článek je na adrese: http://www.codeproject.com/Articles/10750/Fast-Binary-File-Reading-with-C

Při zařazování řadu structs, PtrToStruct získává horní ruky mnohem rychleji, protože si můžete myslet na počtu pole jako pole * délka pole.

Odpovězeno 06/05/2010 v 02:04
zdroj uživatelem

hlasů
3

Neměl jsem štěstí pomocí BinaryFormatter, myslím, že musím mít kompletní struct, který odpovídá obsahu souboru přesně. Zjistil jsem, že nakonec jsem nebyl zájem o moc obsahu souboru stejně tak jsem šel s řešením čtení část proudu do bytebuffer a potom převedením pomocí

Encoding.ASCII.GetString()

pro smyčce a

BitConverter.ToInt32()

Pro celá čísla.

Budu potřebovat, aby bylo možné analyzovat více souboru později, ale pro tuto verzi jsem se dostal pryč s jen pár řádků kódu.

Odpovězeno 06/08/2008 v 10:03
zdroj uživatelem

hlasů
1

Nevidím žádný problém s vašeho kódu.

jen z mé hlavy, co když se pokusíte udělat ručně? funguje to?

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

také pokusit

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

použijte vyrovnávací paměť [] v BinaryReader namísto čtení dat z FileStream, zda budete ještě dostat AccessViolation výjimku.

Neměl jsem štěstí pomocí BinaryFormatter, myslím, že musím mít kompletní struct, který odpovídá obsahu souboru přesně.

To dává smysl, BinaryFormatter má svůj vlastní formát dat, zcela neslučitelné s vašimi.

Odpovězeno 05/08/2008 v 16:31
zdroj uživatelem

hlasů
0

Čtení rovnou do structs je zlo - nejeden C program spadl z důvodu odlišných byte orderings, různé implementace kompilátoru polí, balení, velikost slova .......

Jste nejlepší serialising a deserialising byte po bytu. Použijte sestavení do věcí, pokud chcete, nebo prostě zvyknout BinaryReader.

Odpovězeno 23/09/2008 v 22:43
zdroj uživatelem

hlasů
0

Zkuste to:

using (FileStream stream = new FileStream(fileName, FileMode.Open))
{
    BinaryFormatter formatter = new BinaryFormatter();
    StructType aStruct = (StructType)formatter.Deserialize(filestream);
}
Odpovězeno 05/08/2008 v 15:56
zdroj uživatelem

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