Domanda

Attualmente sto re-progettiamo un'applicazione e sono inciampato su un problema che serializzano alcuni dati.

Dì che ho una serie di dimensioni MXN

double **data;

che voglio serializzare in un

char *dataSerialized

Usando semplici delimitatori (uno per righe, uno per elementi).

De-serializzazione è abbastanza semplice, contando delimitatori e allocare le dimensioni per i dati da memorizzare. Tuttavia, per quanto riguarda la funzione serializzazione, dire

serialize_matrix(double **data, int m, int n, char **dataSerialized);

Quale sarebbe la migliore strategia per determinare le dimensioni necessarie dall'array Char e allocare la memoria appropriata per questo?

forse usando una rappresentazione esponenziale della larghezza fissa di doppia in una stringa? È possibile convertire tutti i byte di doppio in Char's e avere un matrice di Char Slip A (Double) allineata? Come avrei mantenuto intatta l'accuratezza dei numeri?

Nota:

Ho bisogno dei dati in un array di char, non in binario, non in un file.

I dati serializzati verranno inviati tramite la rete utilizzando Zeromq tra un server C e un client Java. Sarebbe possibile, date le dimensioni dell'array e le dimensioni dell'array (doppia) che può essere sempre ricostruita accuratamente tra quei due?

È stato utile?

Soluzione

Java ha un supporto abbastanza buono per leggere byte grezzi e convertirli in quello che vuoi. Puoi decidere un semplice formato wire, quindi serializzare su questo in C e deserializzare in Java.

Ecco un esempio di un formato estremamente semplice, con codice per deserializzare e serializzare.

Ho scritto un programma di test leggermente più grande che posso scaricare da qualche parte se vuoi;crea un array di dati casuale in C, serializza, scrive la stringa serializzata codificata in base64 su stdout.Il programma java molto più piccolo quindi legge, decodifica e deserializza questo.

Codice C da serializzare:

/* 
I'm using this format:
32 bit signed int                   32 bit signed int                   See below
[number of elements in outer array] [number of elements in inner array] [elements]

[elements] is buildt like
[element(0,0)][element(0,1)]...[element(0,y)][element(1,0)]...

each element is sendt like a 64 bit iee754 "double". If your C compiler/architecture is doing something different with its "double"'s, look forward to hours of fun :)

I'm using a couple non-standard functions for byte-swapping here, originally from a BSD, but present in glibc>=2.9.
*/

/* Calculate the bytes required to store a message of x*y doubles */
size_t calculate_size(size_t x, size_t y)
{
    /* The two dimensions in the array  - each in 32 bits - (2 * 4)*/
    size_t sz = 8;  
    /* a 64 bit IEE754 is by definition 8 bytes long :) */
    sz += ((x * y) * 8);    
    /* and a NUL */
    sz++;
    return sz;
}

/* Helpers */
static char* write_int32(int32_t, char*);
static char* write_double(double, char*);
/* Actual conversion. That wasn't so hard, was it? */
void convert_data(double** src, size_t x, size_t y, char* dst)
{

    dst = write_int32((int32_t) x, dst);    
    dst = write_int32((int32_t) y, dst);    

    for(int i = 0; i < x; i++) {
        for(int j = 0; j < y; j++) {
            dst = write_double(src[i][j], dst);
        }
    }
    *dst = '\0';
}


static char* write_int32(int32_t num,  char* c)
{
    char* byte; 
    int i = sizeof(int32_t); 
    /* Convert to network byte order */
    num = htobe32(num);
    byte = (char*) (&num);
    while(i--) {
        *c++ = *byte++;
    }
    return c;
}

static char* write_double(double d, char* c)
{
    /* Here I'm assuming your C programs use IEE754 'double' precision natively.
    If you don't, you should be able to convert into this format. A helper library most likely already exists for your platform.
    Note that IEE754 endianess isn't defined, but in practice, normal platforms use the same byte order as they do for integers.
*/
    char* byte; 
    int i = sizeof(uint64_t);
    uint64_t num = *((uint64_t*)&d);
    /* convert to network byte order */
    num = htobe64(num);
    byte = (char*) (&num);
    while(i--) {
        *c++ = *byte++; 
    }
    return c;
}

Codice Java da annullare la serializzazione:

/* The raw char array from c is now read into the byte[] `bytes` in java */
DataInputStream stream = new DataInputStream(new ByteArrayInputStream(bytes));

int dim_x; int dim_y;
double[][] data;

try {   
    dim_x = stream.readInt();
    dim_y = stream.readInt();
    data = new double[dim_x][dim_y];
    for(int i = 0; i < dim_x; ++i) {
        for(int j = 0; j < dim_y; ++j) {
            data[i][j] = stream.readDouble();
        }
    }

    System.out.println("Client:");
    System.out.println("Dimensions: "+dim_x+" x "+dim_y);
    System.out.println("Data:");
    for(int i = 0; i < dim_x; ++i) {
        for(int j = 0; j < dim_y; ++j) {
            System.out.print(" "+data[i][j]);
        }
        System.out.println();
    }


} catch(IOException e) {
    System.err.println("Error reading input");
    System.err.println(e.getMessage());
    System.exit(1);
}

Altri suggerimenti

Se stai scrivendo un file binario, dovresti pensare a un buon modo per serializzare i dati binari effettivi (64 bit) del tuo double.Questo potrebbe andare dalla scrittura diretta del contenuto del double nel file (minding endianness) ad alcuni schemi di serializzazione di normalizzazione più elaborati (ad esempio con una rappresentazione ben definita di NaN).Dipende davvero da te.Se ti aspetti di trovarti fondamentalmente tra architetture omogenee, un dump della memoria diretto sarebbe probabilmente sufficiente.

Se vuoi scrivere in un file di testo e stai cercando una rappresentazione ASCII, sconsiglierei vivamente una rappresentazione numerica decimale.Invece, potresti convertire i dati grezzi a 64 bit in ASCII usando base64 o qualcosa del genere.

Vuoi davvero mantenere tutta la precisione che hai nel tuo double!

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top