Come dovrei strutturare un'applicazione C # che legge e scrive dati binari?

3

Devo leggere e scrivere "pezzi" binari di circa 1Mb ciascuno. I dati possono arrivare sotto forma di uno stream o di una percentuale in memoria dibyte[].

Normalmente userei Struct con un layout fisso, ma ci sono alcune proprietà di questi dati binari che stanno rendendo questo più difficile:

  1. Il byte 4 descrive la "versione" del blocco
  2. Il byte 5 inizia come array variabile di sotto-blocchi o come blob leggibile dall'uomo
  3. Ogni sotto-blocco ha una versione e ha una matrice variabile di dati al suo interno.
  4. L'endianità dei dati è incoerente

Come dovrei strutturare un programma che deve gestire i dati binari in questo formato?

    
posta random65537 21.03.2013 - 14:50
fonte

2 risposte

3

Una volta ho dovuto implementare un'astrazione di de / serializzazione con versione simile a quello che stai descrivendo in precedenza, descriverò in dettaglio cosa ho fatto (posso ricordare I):

Ho creato una forma non declassata degli oggetti che dovevano essere serializzati / deserializzati che decoravano semplicemente l'ultima versione, quindi tutto il codice che consumava indicava sempre quelli senza versione che erano sempre le ultime versioni; in questo modo quando uscivano nuove versioni non dovevo modificare alcun codice che consumava, solo quelle astrazioni senza versione dovevano essere puntate alle nuove versioni.

Quindi diciamo un esempio come ..

public class Person : V4.Person { }

Poi nell'implementazione, ogni oggetto versioned ha eseguito l'upconversing della deserializzazione passando al precedente finché uno di questi non ha riconosciuto la versione, quindi era qualcosa di simile ..

public class V4.Person : V3.Person
{
    public string SomethingNewInV4 { get; set; }

    public static Person FromBytes(BinaryReader personReader)
    {
        Person person = base.FromBytes(personReader);

        if (person.Version >= 4) // If this version or newer, it will have the SomethingNewInV4 property, deserialize it.
        {
            int stringLength = personReader.ReadInt();
            person.SomethingNewInV4 = personReader.ReadString(stringlength);
        }
        else
        {
            person.SomethingNewInV4 = "default string used when upconverting from V3";
        }

        return person;
    }
}

In questo modo, ogni nuova versione è solo una modifica del precedente, eredita la versione precedente e ne aggiunge (o nasconde) le parti, e usa la sua serializzazione / deserializzazione che poi aggiunge alle sue parti particolari speciali per la sua versione . Esiste la possibilità di un cambio di rotta nella catena, ma ciò significa solo che quella particolare versione potrebbe dover reimplementare la serializzazione / deserializzazione da zero includendo tutta l'intelligenza per la ramificazione se le versioni precedenti dovessero farlo o meno, ma scenari del genere sono probabilmente minimi.

La facciata senza versioni frontali assicura che tutte le nuove serializzazioni siano serializzate nel formato più recente e tutte le vecchie deserializzazioni inizino nella parte superiore dello stack che chiamerà in fondo all'ereditarietà, e ogni versione verrà convertita da lì, in modo che è necessaria la reindirizzamento senza versione delle versioni successive (a meno che ogni volta che esce una nuova versione si desideri aggiornare tutto il codice che consuma).

V1 dovrà eseguire il picking della versione e la deserializzazione completa ecc., ma sopra ogni versione controllerà la versione e sceglierà se i dati sono disponibili o se è necessario eliminarli.

Quando l'ho fatto, anche il subchunking a cui ti riferisci era totalmente lì e ho gestito questo avendo lo stesso layout per ogni tipo di subchunk, così potresti avere la chiamata di deserializzazione di Person al deserializzatore di Arm quando è arrivato al braccio pezzo, e se avesse un numero nella parte anteriore contando le braccia lo userebbe per deserializzare in un ciclo. Ogni subchunk versione ha bisogno di un front-version senza versione dove tutti i principali chunks chiamano, quindi V4.Person chiamerebbe al deserializer di Arm senza versione che potrebbe puntare a V3 se questo è l'ultimo Arm, o ecc.

Ultimo punto: suggerisco caldamente un flusso di memoria per questo, mi ricordo di andare avanti e indietro su questo e rendermi conto che l'array di byte era terribile perché il movimento in avanti significava che dovevo passare i puntatori tra le versioni, con il flusso di memoria versione 1 tutti i dati iniziali V1, V2 prende tutti i dati V2 che sono sovrapposti, e così via e così via senza la necessità di mantenere il puntatore alla posizione corrente nell'array.

Il tuo problema è quasi un passo alla volta identico a quello che ho affrontato e risolto in questo modo, ha funzionato abbastanza bene, spero che questo aiuti! Per rendere le cose un po 'più semplici ho realizzato i metodi di estensione su BinaryReader per alcuni dei tipi che puntavano verso la roba senza versione, quindi ci sarebbe stato come

public Arm ReadArm(this BinaryReader target) { return Arm.FromBytes(target); }

allora di persona avresti ...

int numberOfArms = personReader.ReadInt();
List<Arm> arms = new List<Arm>();
for(int i = 0; i < 1; i++) arms.Add(personReader.ReadArm());

L'ho adattato alla normale denominazione di BinaryReader, non ricordo se è Read o To .. In entrambi i casi si trattava di una comoda scorciatoia.

    
risposta data 21.03.2013 - 15:39
fonte
0

Mi chiedo se una sorta di configurazione di lexer / parser sarebbe in ordine, poiché:

  • sembra che tu stia passando da un flusso di input (di qualche tipo) a una struttura ad albero (se sto leggendo le cose a posto)

  • sarebbe un modo per eseguire il back-tracking che hai menzionato potrebbe essere necessario fare

  • potrebbe probabilmente gestire parte dell'incertezza di endianità / passaggio che potresti incontrare

Non so se potresti usarne uno pre-scritto in questo caso d'uso, ma potrebbe essere un punto di partenza per pensare a strutturare le cose.

    
risposta data 21.03.2013 - 16:58
fonte

Leggi altre domande sui tag