Manipulando Arquivo Binário em Java e C++

Pessoal, olha que problema estranho em manipulação de arquivos usando c++ e Java:

Tenho um programa em c++ que grava a seguinte struct em arquivo:

struct
{
	long Tamanho;
	int codigo;
} Cabecalho

A parte de gravação dos dados (após setados os valores) no disco é:

HFILE  File;
OFSTRUCT Of;
Cabecalho Dados;

File = OpenFile ( (LPCSTR) Nome.c_str(), &Of, OF_WRITE);
_lwrite(File,(char *)&Dados,sizeof(Dados));
_lclose(File);

E uma função em Java para ler esse arquivo:

private void openFile () {
			
		JFileChooser fileChooser = new JFileChooser ();
		fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
		int result  = fileChooser.showSaveDialog(this);
		
		if (result == JFileChooser.CANCEL_OPTION){
			System.out.println("Clicado CANEL");
		}
		
		File fileName = fileChooser.getSelectedFile();
		
		if (fileName == null || fileName.getName().equals("")){
			System.out.println("Erro no nome do arquivo");
		}
		else {
			try {
				file = new RandomAccessFile(fileName,"rw");	
			        long tamanho = file.readLong();	
                                int codigo = file.readInt();	
                                System.out.println(tamanho);
                                System.out.println(codigo);
			} catch (FileNotFoundException e) {				
				e.printStackTrace();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

O problema todo é que quando a função Java lê o arquivo, os valores encontrados são diferentes dos valores gravados pela função C++.

Alguém sabe por que isso ocorre e principalmente, qual a forma correta de se lêr esses dados?

Obrigado aos que ajudarem…

Ulisses Nunes

Dicas:

a) long do C++ = 4 bytes, long do Java = 8 bytes. Portanto você tem de usar readInt, não readLong
b) Se estiver usando uma máquina Intel ou AMD (ou Via, ou sei lá que compatível com o Pentium que você tem), a ordem dos bytes do C++ é ao inverso da ordem dos bytes do Java. Se em C++ você tiver um int com o valor em hexadecimal 0x12345678 (decimal 305419896), em Java esse valor será lido como 0x78563412 (decimal 2018915346). Ou seja, você vai ter de pegar os bytes e inverter a ordem.

Uma forma de inverter os bytes (embora não seja a mais rápida - eu sei que existem uns truques federais, mas não estou conseguindo encontrá-los com facilidade) é esta (confira por favor, estou fazendo de cabeça)

int x = 0x11223344; 
x = ((x >> 24) & 0xFF ) |
       (x >>   8) & 0xFF00) |
       (x <<   8) & 0xFF0000) |
       (x << 24) & 0xFF000000));
System.out.println (Integer.toHexString (x)); // deveria imprimir 44332211.

Sua função de shift funciona perfeitamente. Se não for abusar muito poderia me passar a mesma para conversão de variaveis long?

O “long” do C++ é o “int” do Java (pelo menos em Windows e Linux 32 bits), portanto não é preciso fazer uma outra função de shift.

Funcionou perfeitamente, obrigado pela ajuda.

Agora, tenho outra questão…

e como faria para ler um char[100] que representa o nome do Cliente?? E tipos WORD e DWORD em C++, como são representados em Java?

Att. Ulisses Nunes

char © = byte (Java).

Faça o seguinte: pegue os 100 caracteres © e leia em um array de 100 bytes (Java). Você pode então converter os bytes para uma String usando new String (bytes, “ISO-8859-1”). Como uma string em C termina por ‘\0’, você tem de encontrar o primeiro ‘\0’ nessa string e “podar” a string Java resultante nessa posição (use indexOf).

WORD © = short (Java)
DWORD © = int (Java)

Aproveitando a aula, fica faltando saber a correspondência em Java dos tipos float e double em C++.

float © = float (Java)
double © = double (Java)

Problema: como a ordem dos bytes é invertida, você teria de “desinverter”, só que o processo é mais complexo. Te digo mais tarde como é que se faz.

Código em C++ para criar um arquivo com 1 double e 1 float.

#define _CRT_SECURE_NO_DEPRECATE
#define _USE_MATH_DEFINES
#include <cstdio>
#include <cmath>
using namespace std;
int main (int argc, char *argv[]) {
    FILE* f = fopen ("teste.bin", "wb");
    double dbl = M_PI; // 3.14159265358979323846
    float flt = M_E; // 2.71828182845904523536
    fwrite (&dbl, sizeof(dbl), 1, f);
    fwrite (&flt, sizeof(flt), 1, f);
    fclose (f);
}

Código em Java para ler esse arquivo.

import java.io.*;

class DoubleCJava {
    public static void main(String[] args) throws Exception {
        DataInputStream dis = new DataInputStream (new FileInputStream ("teste.bin"));
        long _dbl = dis.readLong();
        int _flt = dis.readInt();
        dis.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));
        float flt = Float.intBitsToFloat(_flt);
        double dbl = Double.longBitsToDouble(_dbl);
        System.out.println (dbl); // imprime 3.141592653589793
        System.out.println (flt); // imprime 2.7182817
    }
}

Funcionou tudo perfeitamente, obrigado.