Pergunta

No momento, estou redesenhando um aplicativo e me deparei com um problema ao serializar alguns dados.

Digamos que eu tenha uma matriz de tamanho mxn

double **data;

que desejo serializar em um

char *dataSerialized

usando delimitadores simples (um para linhas, um para elementos).

A desserialização é bastante direta, contando delimitadores e alocando o tamanho dos dados a serem armazenados. No entanto, e quanto à função serializar, digamos

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

Qual seria a melhor estratégia para determinar o tamanho necessário para o array char e alocar a memória apropriada para ele?

Talvez usando alguma representação exponencial de largura fixa de double's em uma string? É possível apenas converter todos os bytes de double em char e ter um array de char alinhado sizeof (double)? Como manteria a precisão dos números intacta?

NOTA:

Preciso dos dados em uma matriz de char, não em binário, não em um arquivo.

Os dados serializados serão enviados pela rede usando ZeroMQ entre um servidor C e um cliente Java. Seria possível, dadas as dimensões do array e sizeof (double), que ele sempre possa ser reconstruído com precisão entre os dois?

Foi útil?

Solução

Java tem um suporte muito bom para leitura de bytes brutos e conversão para o que você quiser. Você pode decidir por um formato de ligação simples e, em seguida, serializar em C e desserializar em Java.

Aqui está um exemplo de formato extremamente simples, com código para desserializar e serializar.

Eu escrevi um programa de teste um pouco maior que posso despejar em algum lugar se você quiser;ele cria uma matriz de dados aleatórios em C, serializa, grava a string serializada codificada em base64 em stdout.O programa java muito menor então lê, decodifica e desserializa isso.

Código C para serializar:

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

Código Java para desserializar:

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

Outras dicas

Se você está escrevendo um arquivo binário, deve pensar em uma boa maneira de serializar os dados binários reais (64 bits) do seu double.Isso poderia ir desde a gravação direta do conteúdo do duplo no arquivo (considerando o endianness) até alguns esquemas de serialização de normalização mais elaborados (por exemplo, com uma representação bem definida de NaN).Isso depende de você realmente.Se você espera estar basicamente entre arquiteturas homogêneas, um despejo de memória direto provavelmente seria suficiente.

Se você deseja escrever em um arquivo de texto e está procurando uma representação ASCII, eu desencorajo fortemente uma representação numérica decimal.Em vez disso, você pode converter os dados brutos de 64 bits em ASCII usando base64 ou algo parecido.

Você realmente deseja manter toda a precisão que tem em seu double!

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top