سؤال

أقوم حاليًا بإعادة تصميم أحد التطبيقات وواجهت مشكلة في إجراء تسلسل لبعض البيانات.

لنفترض أن لدي مجموعة من الحجم mxn

double **data;

الذي أريد إجراء تسلسل فيه إلى ملف

char *dataSerialized

باستخدام محددات بسيطة (واحدة للصفوف، وواحدة للعناصر).

تعد عملية إلغاء التسلسل واضحة إلى حد ما، حيث يتم حساب المحددات وتخصيص حجم البيانات المراد تخزينها.ومع ذلك، ماذا عن وظيفة التسلسل، على سبيل المثال

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

ما هي أفضل استراتيجية لتحديد الحجم الذي تحتاجه مصفوفة char وتخصيص الذاكرة المناسبة لها؟

ربما يتم استخدام بعض التمثيل الأسي ذو العرض الثابت للمضاعفات في السلسلة؟هل من الممكن فقط تحويل كافة وحدات البايت المزدوجة إلى char والحصول على صفيف char محاذي بحجم (مزدوج)؟كيف يمكنني الحفاظ على دقة الأرقام سليمة؟

ملحوظة:

أحتاج إلى البيانات في مصفوفة char، وليس في ملف ثنائي.

سيتم إرسال البيانات المتسلسلة عبر الشبكة باستخدام ZeroMQ بين خادم C وعميل Java.هل من الممكن، بالنظر إلى أبعاد المصفوفة وحجمها (المزدوج) أنه يمكن دائمًا إعادة بنائها بدقة بين هذين الاثنين؟

هل كانت مفيدة؟

المحلول

تتمتع Java بدعم جيد جدًا لقراءة البايتات الأولية والتحويل إلى ما تريد.يمكنك اتخاذ قرار بشأن تنسيق سلكي بسيط، ثم إجراء تسلسل لهذا في لغة C، وإلغاء التسلسل في Java.

فيما يلي مثال لتنسيق بسيط للغاية، مع تعليمات برمجية لإلغاء التسلسل وإجراء تسلسل.

لقد قمت بكتابة برنامج اختبار أكبر قليلًا ويمكنني التخلص منه في مكان ما إذا أردت؛يقوم بإنشاء مصفوفة بيانات عشوائية في لغة C، وإجراء تسلسل لها، وكتابة السلسلة المتسلسلة المشفرة base64 إلى stdout.ثم يقوم برنامج جافا الأصغر بكثير بقراءة هذا وفك تشفيره وإلغاء تسلسله.

رمز C للتسلسل:

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

كود جافا لإلغاء التسلسل:

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

نصائح أخرى

إذا كنت تكتب ملفًا ثنائيًا، فيجب أن تفكر في طريقة جيدة لإجراء تسلسل للبيانات الثنائية الفعلية (64 بت) الخاصة بك double.يمكن أن ينتقل هذا من الكتابة المباشرة لمحتوى المضاعفة إلى الملف (مع مراعاة endianness) إلى بعض مخططات التسلسل التطبيعية الأكثر تفصيلاً (على سبيل المثال.مع تمثيل محدد جيدًا لـ NaN).هذا متروك لك حقا.إذا كنت تتوقع أن تكون بشكل أساسي بين البنى المتجانسة، فمن المحتمل أن يكون تفريغ الذاكرة المباشر كافيًا.

إذا كنت تريد الكتابة إلى ملف نصي وتبحث عن تمثيل ASCII، فأنا لا أشجع بشدة على التمثيل الرقمي العشري.بدلاً من ذلك، يمكنك تحويل البيانات الأولية 64 بت إلى ASCII باستخدام base64 أو شيء من هذا القبيل.

أنت حقًا تريد الاحتفاظ بكل الدقة التي لديك double!

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top