Pregunta

Según aquí , el compilador de C rellenará los valores al escribir una estructura a un archivo binario. Como dice el ejemplo en el enlace, al escribir una estructura como esta:

struct {
 char c;
 int i;
} a;

a un archivo binario, el compilador generalmente dejará un agujero sin nombre y sin usar entre los campos char e int, para asegurarse de que el campo int esté alineado correctamente.

¿Cómo podría crear una réplica exacta del archivo de salida binario (generado en C), usando un lenguaje diferente (en mi caso, Java)?

¿Existe una forma automática de aplicar el relleno C en la salida de Java? ¿O tengo que revisar la documentación del compilador para ver cómo funciona (el compilador es g ++ por cierto).

¿Fue útil?

Solución

Esto es cierto no solo cuando se escribe en archivos, sino también en la memoria. Es el hecho de que la estructura está rellenada en la memoria, lo que hace que el relleno se muestre en el archivo, si la estructura se escribe byte por byte.

En general, es muy difícil replicar con certeza el esquema de relleno exacto, aunque supongo que algunas heurísticas lo llevarían bastante lejos. Ayuda si tiene la declaración de estructura, para análisis.

Normalmente, los campos de más de un carácter se alinearán de modo que su desplazamiento inicial dentro de la estructura sea un múltiplo de su tamaño. Esto significa que short s generalmente estará en compensaciones pares (divisible por 2, asumiendo sizeof (short) == 2 ), mientras que double s estar en desplazamientos divisibles por 8, y así sucesivamente.

ACTUALIZACIÓN : es por razones como esta (y también por razones relacionadas con la endianidad) que generalmente es una mala idea volcar estructuras completas en archivos. Es mejor hacerlo campo por campo, así:

put_char(out, a.c);
put_int(out, a.i);

Suponiendo que las funciones put solo escriben los bytes necesarios para el valor, esto emitirá una versión sin relleno de la estructura en el archivo, resolviendo el problema. También es posible garantizar un orden de bytes adecuado y conocido escribiendo estas funciones en consecuencia.

Otros consejos

No hagas esto, es frágil y provocará errores de alineación y resistencia.

Para datos externos, es mucho mejor definir explícitamente el formato en términos de bytes y escribir funciones explícitas para convertir entre formato interno y externo, usando shift y máscaras (¡no unión!).

  

¿Existe una forma automática de aplicar C   relleno en la salida de Java? O tengo   pasar por la documentación del compilador   para ver cómo funciona (el compilador es   g ++ por cierto).

Ninguno de los dos. En cambio, especifica explícitamente un formato de datos / comunicación e implementa esa especificación, en lugar de depender de los detalles de implementación del compilador de C. Ni siquiera obtendrá la misma salida de diferentes compiladores de C.

Para interoperabilidad, mire la clase ByteBuffer.

Esencialmente, crea un búfer de cierto tamaño, coloca () variables de diferentes tipos en diferentes posiciones y luego llama a array () al final para recuperar el " raw " representación de datos:

ByteBuffer bb = ByteBuffer.allocate(8);
bb.order(ByteOrder.LITTLE_ENDIAN);
bb.put(0, someChar);
bb.put(4, someInteger);
byte[] rawBytes = bb.array();

Pero depende de usted decidir dónde colocar el relleno, es decir, cuántos bytes omitir entre las posiciones.

Para leer datos escritos desde C, generalmente ajusta () un ByteBuffer alrededor de una matriz de bytes que ha leído de un archivo.

En caso de que sea útil, he escrito más en ByteBuffer .

Una forma práctica de leer / escribir estructuras C en Java es usar la clase javolution Struct (consulte http: // www .javolution.org ). Esto no lo ayudará a rellenar / alinear automáticamente sus datos, pero hace que trabajar con datos sin procesar almacenados en un ByteBuffer sea mucho más conveniente. Si no está familiarizado con la javolución, vale la pena echarle un vistazo, ya que también hay muchas otras cosas interesantes allí.

Este agujero es configurable, el compilador tiene interruptores para alinear estructuras por 1/2/4/8 bytes.

Entonces, la primera pregunta es: ¿Qué alineación quieres simular exactamente?

Con Java, el tamaño de los tipos de datos está definido por la especificación del lenguaje. Por ejemplo, un tipo byte es 1 byte, short es 2 bytes, y así sucesivamente. Esto es diferente a C, donde el tamaño de cada tipo depende de la arquitectura.

Por lo tanto, sería importante saber cómo se formatea el archivo binario para poder leer el archivo en Java.

Puede ser necesario tomar medidas para asegurarse de que los campos son de un tamaño específico, para tener en cuenta las diferencias en el compilador o la arquitectura. La mención de la alineación parece sugerir que el archivo de salida dependerá de la arquitectura.

puedes probar preon :

  

Preon es una biblioteca de Java para construir códecs para datos comprimidos de flujo de bits en un   forma declarativa (basada en anotaciones). Piense en JAXB o Hibernate, pero luego en binario   datos codificados.

puede manejar datos binarios endian Big / Little, alineación (relleno) y varios tipos numéricos junto con otras características. Es una biblioteca muy bonita, me gusta mucho

mi 0.02 $

Recomiendo encarecidamente búferes de protocolo para exactamente este problema.

Según tengo entendido, estás diciendo que no controlas la salida del programa C. Tienes que tomarlo como se da.

Entonces, ¿tiene que leer este archivo para un conjunto específico de estructuras, o tiene que resolver esto en un caso general? Quiero decir, ¿es el problema que alguien dijo, "Aquí está el archivo creado por el programa X, tienes que leerlo en Java"? ¿O esperan que su programa Java lea el código fuente C, encuentre la definición de estructura y luego lo lea en Java?

Si tiene un archivo específico para leer, el problema no es realmente muy difícil. Revisando las especificaciones del compilador de C o estudiando archivos de ejemplo, descubra dónde está el relleno. Luego, en el lado de Java, lea el archivo como una secuencia de bytes y cree los valores que sabe que vendrán. Básicamente, escribiría un conjunto de funciones para leer el número requerido de bytes de un InputStream y convertirlos en el tipo de datos apropiado. Me gusta:

int readInt(InputStream is,int len)
  throws PrematureEndOfDataException
{
  int n=0;
  while (len-->0)
  {
    int i=is.read();
    if (i==-1)
      throw new PrematureEndOfDataException();
    byte b=(byte) i;
    n=(n<<8)+b;
  }
  return n;
}

Puede alterar el empaque en el lado c para asegurarse de que no se use relleno, o también puede mirar el formato de archivo resultante en un editor hexadecimal para permitirle escribir un analizador en Java que ignore los bytes que están rellenando.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top