2016-12-09 12 views
4

порядок байтов Native код:FWRITE() в C & readInt() в Java отличаются

написание номер 27, используя FWRITE().

int main() 
{ 
    int a = 27; 
    FILE *fp; 
    fp = fopen("/data/tmp.log", "w"); 
    if (!fp) 
    return -errno; 

    fwrite(&a, 4, 1, fp); 
    fclose(); 
    return 0; 
} 

Чтение обратно данные (27) с использованием DataInputStream.readInt():

public int readIntDataInputStream(void) 
{ 
    String filePath = "/data/tmp.log"; 
    InputStream is = null; 
    DataInputStream dis = null; 
    int k; 

    is = new FileInputStream(filePath); 
    dis = new DataInputStream(is); 
    k = dis.readInt(); 
    Log.i(TAG, "Size : " + k); 
    return 0; 
} 

О/р

Size : 452984832 

Хорошо, что в шестнадцатеричном является 0x1b000000

0x1b27. Но readInt() читает данные как большие endian, в то время как моя родная кодировка написана как маленькая endian. , Итак, вместо 0x0000001b я получаю 0x1b000000.

Правильно ли я понимаю? Кто-нибудь сталкивался с этой проблемой раньше?

+1

Да, вы правы. C будет писать в endianness CPU, что для x86-процессоров малопригодно. ['DataInputStream.readInt()'] (https://docs.oracle.com/javase/8/docs/api/java/io/DataInput.html#readInt--) всегда будет читать big-endian. Решение. Определите, какая информация должна иметь ваш файл, и убедитесь, что оба действуют соответственно. – Andreas

+2

Понятно, что файл должен быть большим, что делает его переносимым * и * совместимым с Java и соответствующим образом скорректирует код C. Все, что вам нужно в этом C-коде, это 'int a = htonl (27);' – EJP

+0

Спасибо @Andreas. У меня есть большое количество данных для записи. Как я могу справиться с этим эффективно в C? –

ответ

2

Из Javadoc для readInt():

Этот метод подходит для чтения байтов, написанные writeInt методом интерфейса DataOutput

Если вы хотите что-то, написанное в C программу, которую вы»прочитать Вам нужно будет поменять байты самостоятельно, используя объекты в java.nio. Я никогда не делал этого, но я полагаю, что вы прочитали данные в ByteBuffer, установите порядок буфера на ByteOrder.LITTLE_ENDIAN, а затем создайте вид IntBuffer над ByteBuffer, если у вас есть массив значений или просто используйте ByteBuffer#getInt() для одного значения.

Все, что в стороне, я согласен с @EJP в том, что внешний формат данных должен быть большим для большей совместимости.

+0

'ByteBuffer' имеет [' getInt() '] (https://docs.oracle.com/javase/8/docs/api/java/nio/ByteBuffer.html # getInt--), чтобы читать следующие 4 байта как 'int' в заданной сущности. Представление «IntBuffer» полезно, только если все данные «int», например. если это 'int []'. – Andreas

+0

@ Аndreas спасибо, я отредактировал в вашей информации. –

0

Есть несколько вопросов, в вашем коде:

  • Вы предполагаете, что размер int является 4, это не всегда верно, и так как вы хотите иметь дело с 32-битным Интсом, вы должны использовать int32_t или uint32_t.

  • Вы должны открыть файл в двоичном файле больше, чтобы надежно записать двоичные данные. Вышеприведенный код не будет работать в Windows для менее тривиального вывода. Используйте fopen("/data/tmp.log", "wb").

  • Вы должны иметь дело с контентом. Вы используете этот файл для обмена данными между различными платформами, которые могут иметь разные атрибуты native endianness и/или endian API. Java, похоже, использует big-endian, aka сетевой порядок байтов, поэтому вы должны преобразовать значения на платформе C с помощью функции полезности hton32(). Это вряд ли будет иметь существенное влияние на производительность на стороне ПК, так как эта функция обычно расширяется внутри, возможно, как одна инструкция, и большая часть времени будет потрачена на ожидание ввода-вывода в любом случае.

Вот модифицированная версия кода:

#include <endian.h> 
#include <stdint.h> 
#include <stdio.h> 

int main(void) { 
    uint32_t a = hton32(27); 
    FILE *fp = fopen("/data/tmp.log", "wb"); 
    if (!fp) { 
     return errno; 
    } 
    fwrite(&a, sizeof a, 1, fp); 
    fclose(); 
    return 0; 
} 
+0

привет chqrlie, спасибо за ответ. Что касается пунктов 1 и 2, я знаю об этих вещах. Кроме того, dis z только тестовый код. Точка 1 -> Я воспринял ее как 4 байта, потому что она упоминается, readInt() java все равно будет читать ровно 4 байта. 2-> Я работаю для Unix-систем. В системах Unix «b» в fopen не имеет никакой значимости. Из man-страницы «Это строго для совместимости с C89 и не имеет никакого эффекта:« b »игнорируется на всех совместимых с POSIX системах, включая Linux». Но, это хорошие моменты, чтобы сделать программу элегантной. Благодарю. –

+0

@mk ..: Я понимаю, что опубликованный код - это просто быстрый и грязный тест. Я всегда стараюсь дать подробный ответ не только OP, но и другим читателям, чтобы увидеть все возможные проблемы. '' wb "' строго эквивалентен '' w "' на большинстве платформ Unix, но не больно использовать 'b', и это делает более очевидным, что' '/data/tmp.log' 'является двоичным файлом , которое название не подразумевает. 'int '- 32-разрядная длина в подавляющем большинстве систем Unix, но размер' long' (64-бит в java) варьируется в зависимости от ABI, даже на одном и том же хосте (32-битный 64-разрядный режим). Элегантность должна стать второй натурой. – chqrlie