Question

Je remets actuellement une application et je suis tombé sur un problème sérialisant certaines données.

Dis que j'ai un tableau de taille mxn

double **data;

que je veux sérialiser dans un

char *dataSerialized

Utilisation de délimiteurs simples (un pour les lignes, un pour les éléments).

La désérialisation est assez simple, en comptant les délimiteurs et en allouant la taille aux données à stocker. Cependant, qu'en est-il de la fonction de sérialisation, disons

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

Quelle serait la meilleure stratégie pour déterminer la taille nécessaire par le tableau char et allouer la mémoire appropriée pour cela?

Peut-être en utilisant une représentation exponentielle de largeur fixe de Double's dans une chaîne? Est-il possible de convertir tous les octets de Double en Char et d'avoir une taille de char alignée (double)? Comment pourrais-je garder la précision des chiffres intacts?

REMARQUE:

J'ai besoin des données dans un tableau char, pas en binaire, pas dans un fichier.

Les données sérialisées seront envoyées sur le réseau à l'aide de Zeromq entre un serveur C et un client Java. Serait-il possible, étant donné les dimensions du tableau et la taille (double) qu'il peut toujours être reconstruit avec précision entre ces deux?

Était-ce utile?

La solution

Java a un assez bon soutien pour lire les octets bruts et convertir ce que vous voulez. Vous pouvez décider d'un simple format filaire, puis sérialiser à cela en C, et non désérialisé en Java.

Voici un exemple de format extrêmement simple, avec du code pour non désérialisé et sérialiser.

J'ai écrit un programme de test légèrement plus grand que je peux vider quelque part si vous le souhaitez; Il crée un tableau de données aléatoire en C, sérialise, écrit la chaîne sérialisée Base64 codée en stdout. Le programme Java beaucoup plus petit lit, décode et désérialise alors cela.

C code pour sérialiser:

/* 
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;
}

Code java à se non désérialisé:

/* 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);
}

Autres conseils

Si vous écrivez un fichier binaire, vous devez penser à un bon moyen de sérialiser les données binaires réelles (64 bits) de votre double. Cela pourrait passer de l'écriture directement du contenu du double au fichier (Minding Endianness) à certains schémas de sérialisation normalisants plus élaborés (par exemple avec une représentation bien définie de NAN). C'est à toi vraiment. Si vous vous attendez à être essentiellement parmi les architectures homogènes, un dépotoir de mémoire direct suffirait probablement.

Si vous souhaitez écrire dans un fichier texte et que A recherche une représentation ASCII, je découragerais fortement une représentation numérique décimale. Au lieu de cela, vous pouvez convertir les données brutes 64 bits en ASCII en utilisant Base64 ou quelque chose comme ça.

Vous voulez vraiment garder toute la précision que vous avez dans votre double!

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top