Estou tendo dificuldades em ler um arquivo binário que foi gravado por um programa feito em C. Os campos texto eu consigo ler, mas os campos int, double e outros tipos numéricos não lê o mesmo valor que foi gravado.
Isso foi respondido em algum tópico antes do “crash” do GUJ…
Acontece que o int do C não ocupa necessariamente o mesmo número de bits do int do Java. Tente descobrir qual o tamanho do int do seu compilador C e então use um readByte no java, então, a cada vez que acumular o nº de bytes certo, jogue o valor acumulado em um int.
D
daniel.costa
o int do C++ (Win32) ocupa 4 bytes e o do JAVA também ocupa 4 bytes
Fox_McCloud
Então verifique os outros tipos: double, etc…
Esse tipo de problema acontece porque o dado começa a ser lido na posição errada do arquivo, ou termina de ser lido na posição errada do arquivo (ou os dois).
O valor booleano, por exemplo, em Java, ocupa nº de bytes diferentes dependendo da máquina virtual que está sendo utilizada.
Será que o int não está sendo pego a partir de um resto do String, como um caractere ‘\0’, por exemplo? Se não me engano o C e o Java não interpretam o ‘\0’ da mesma forma.
T
thingol
Por exemplo, suponha que você tenha um arquivo que foi gerado pelo seguinte programa C++:
importjava.io.*;classDoubleCJava{publicstaticvoidmain(String[]args)throwsException{DataInputStreamdis=newDataInputStream(newFileInputStream("teste.bin"));long_dbl=dis.readLong();// lê 8 bytesint_flt=dis.readInt();// lê 4 bytesdis.close();_flt=((_flt>>>24&0x000000FF)|(_flt>>>8&0x0000FF00)|(_flt<<8&0x00FF0000)|(_flt<<24&0xFF000000));_dbl=((_dbl>>>56&0x00000000000000FFL)|(_dbl>>>40&0x000000000000FF00L)|(_dbl>>>24&0x0000000000FF0000L)|(_dbl>>>8&0x00000000FF000000L)|(_dbl<<8&0x000000FF00000000L)|(_dbl<<24&0x0000FF0000000000L)|(_dbl<<40&0x00FF000000000000L)|(_dbl<<56&0xFF00000000000000L));floatflt=Float.intBitsToFloat(_flt);doubledbl=Double.longBitsToDouble(_dbl);System.out.println(dbl);// imprime 3.141592653589793System.out.println(flt);// imprime 2.7182817}}
Fox_McCloud
Como funcionam esses deslocamentos binários?
Eu não poderia imprimir o sizeof(flt), usar um byte[] e readByte() e jogar o valor em um float quando atingisse o tamanho do sizeof?
T
thingol
Esse monte de deslocamentos é porque a ordem dos bytes em máquinas compatíveis com Intel (little-endian) é ao contrário da ordem que o Java espera os bytes em um DataInput/OutputStream (big-endian).
E de qualquer maneira a função de conversão da representação IEEE de um float ou double requer o uso de um int ou um long.
Fox_McCloud
Hum… muito interessante, eu não sabia disso… :-o
T
thingol
Se o arquivo foi gerado em uma máquina Sparc (Sun, Fujitsu), em uma HP PA-RISC ou em uma IBM PowerPC (ou então em um Mac não-Intel), então não é necessário fazer esses shifts todos (são big-endian).
Só as máquinas Intel (e se você usa Digital Alpha ou Intel Itanium em modo little-endian) é que pensam “ao contrário”.
O problema é que a maior parte dos computadores usam chips que são Intel ou compatíveis…
Fox_McCloud
Que a Intel não segue o padrão eu sabia.
Mas eu não havia parado para associar que o Java seguiria o padrão (Big Endian) o que, claro, é natural…
É que a gente vai ficando tão acostumado com ambientes Intel (e Micro$oft)…
De qualquer forma eu nunca precisei trocar informações binárias entre C e Java, o mais próximo que eu já cheguei disso foram pequenas manipulações em um arquivo .wav, sendo que formatos de áudio também podem obedecer a um dos dois padrões (big ou little endian)…
T
thingol
Em Java a ordem dos bytes é importante em:
DataInputStream/DataOutputStream
Serialização (que indiretamente usa essas rotinas readInt/readShort/readDouble/readFloat/readLong).
Se o arquivo original foi criado em uma máquina big-endian, pode-se usar diretamente readFloat e readDouble em vez de usar essa “gambiarra” que mostrei acima.
Fox_McCloud
É… basicamente o que isso faz é desinverter o código binário, certo?
Inverte 32 bits (4 bytes) para o float e 64 bits (8 bytes) para o double.
Inversão por deslocamentos…
Ficou claro!
obs: como não existe uma utilidade nativa para isso?
T
thingol
Ou seja, esqueci de efetuar um "refactoring" no código acima para evitar esses "shifts" sem explicação.
importjava.io.*;classDoubleCJava{/** * Inverte os bytes de um int */publicstaticintinverteBytes(intx){return((x>>>24&0x000000FF)|(x>>>8&0x0000FF00)|(x<<8&0x00FF0000)|(x<<24&0xFF000000));}/** * Inverte os bytes de um long */publicstaticlonginverteBytes(longx){return((x>>>56&0x00000000000000FFL)|(x>>>40&0x000000000000FF00L)|(x>>>24&0x0000000000FF0000L)|(x>>>8&0x00000000FF000000L)|(x<<8&0x000000FF00000000L)|(x<<24&0x0000FF0000000000L)|(x<<40&0x00FF000000000000L)|(x<<56&0xFF00000000000000L));}/** * Este programa lê um double e um float que foram gravados por um programa em C * em ambiente Intel (little-endian). */publicstaticvoidmain(String[]args)throwsException{DataInputStreamdis=newDataInputStream(newFileInputStream("teste.bin"));long_dbl=dis.readLong();int_flt=dis.readInt();dis.close();_dbl=inverteBytes(_dbl);_flt=inverteBytes(_flt);doubledbl=Double.longBitsToDouble(_dbl);floatflt=Float.intBitsToFloat(_flt);System.out.println(dbl);// imprime 3.141592653589793System.out.println(flt);// imprime 2.7182817}}