2016-12-09 2 views
4

code natif:fwrite() dans C & readInt() en Java diffèrent dans endianess

Numéro d'écriture 27 en utilisant 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; 
} 

de lecture avant les données (27) à l'aide de 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; 
} 

O/p

Size : 452984832 

Bien que dans l'hexagone est 0x1b000000

0x1b est 27. Mais readInt() lit les données en big endian alors que mon code natif écrit en petit boutiste. . Donc, au lieu de 0x0000001b je reçois 0x1b000000.

Est-ce que je comprends bien? Quelqu'un at-il déjà rencontré ce problème auparavant?

+1

Oui, vous avez raison. C écrira en endianness de CPU, qui pour les processeurs x86 est little-endian. ['DataInputStream.readInt()'] (https://docs.oracle.com/javase/8/docs/api/java/io/DataInput.html#readInt--) lira toujours big-endian. Solution: Déterminez quel endianisme votre fichier devrait avoir, et assurez-vous que les deux agissent en conséquence. – Andreas

+2

Plus précisément, décidez que le fichier doit être big-endian, ce qui le rend portable * et * compatible avec Java, et ajuste le code C en conséquence. Tout ce dont vous avez besoin dans ce code C est 'int a = htonl (27);' – EJP

+0

Merci @Andreas. J'ai de grandes quantités de données à écrire. Comment puis-je gérer cela efficacement en C? –

Répondre

2

De l'Javadoc pour readInt():

Cette méthode est adaptée pour les octets de lecture écrits par la méthode writeInt de l'interface DataOutput

Si vous voulez lire quelque chose écrit par un programme C vous » ll faut faire l'octet vous permuter, en utilisant les installations dans java.nio. Je n'ai jamais fait cela mais je crois que vous lisiez les données dans un ByteBuffer, définissiez l'ordre du tampon à ByteOrder.LITTLE_ENDIAN et ensuite créiez une vue IntBuffer sur ByteBuffer si vous avez un tableau de valeurs, ou utilisez simplement ByteBuffer#getInt() pour une seule valeur. Tout cela mis à part, je suis d'accord avec @EJP que le format externe pour les données devrait être big-endian pour une compatibilité maximale.

+0

'ByteBuffer' a un [' getInt() '] (https://docs.oracle.com/javase/8/docs/api/java/nio/ByteBuffer.html # getInt--) pour lire les 4 octets suivants en tant que 'int' dans l'endianness donné. La vue IntBuffer n'est utile que si toutes les données sont int, par ex. si c'est un 'int []'. – Andreas

+0

@Andreas merci, j'ai édité dans votre information. –

0

Il y a plusieurs questions dans votre code:

  • Vous assumez que la taille de int est 4, il est pas nécessairement vrai, et puisque vous voulez traiter 32 bits ints, vous devez utiliser int32_t ou uint32_t.

  • Vous devez ouvrir le fichier en plus binaire pour écrire des données binaires de façon fiable. Le code ci-dessus échouerait sur Windows pour une sortie moins triviale. Utilisez fopen("/data/tmp.log", "wb").

  • Vous devez faire face à l'endianness. Vous utilisez le fichier pour échanger des données entre différentes plates-formes qui peuvent avoir différentes API endianness et/ou endian spécifiques. Java semble utiliser big-endian, alias byte réseau, donc vous devriez convertir les valeurs sur la plate-forme C avec la fonction utilitaire hton32(). Il est peu probable que cela ait un impact significatif sur les performances du côté PC, car cette fonction est généralement développée en ligne, éventuellement en tant qu'instrument unique et la plupart du temps, elle sera utilisée en attente d'E/S.

Voici une version modifiée du code:

#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

salut chqrlie, Merci pour la réponse. En ce qui concerne le point 1 et le point 2, je suis au courant de ces choses. Aussi, dis z seulement un code de test. Point 1 -> Je l'ai pris comme 4 octets parce qu'il est mentionné, readInt() de java va quand même lire exactement 4 octets. 2-> Je travaille pour les systèmes Unix. Dans les systèmes Unix, 'b' dans fopen n'a aucune signification. De la page de manuel "Ceci est strictement pour la compatibilité avec C89 et n'a aucun effet, le 'b' est ignoré sur tous les systèmes conformes POSIX, y compris Linux." Mais, ce sont de bons points pour rendre le programme élégant. Merci. –

+0

@mk ..: Je comprends que le code affiché est juste un test rapide et sale. J'essaie toujours de fournir une réponse détaillée pour non seulement le PO, mais aussi d'autres lecteurs pour voir tous les problèmes potentiels. '" wb "' est strictement équivalent à '" w "' sur la plupart des plates-formes Unix, mais cela ne fait pas de mal d'utiliser 'b' et il est plus évident que' "/data/tmp.log" 'est un fichier binaire , que le nom n'implique pas. 'int' est long de 32 bits sur la grande majorité des systèmes Unix, mais la taille de' long' (64 bits en java) varie entre les ABI, même sur le même hôte (mode 32 vs 64 bits). L'élégance devrait devenir une seconde nature. – chqrlie