Serialice la matriz int en forma binaria / base64 para usarla en VTK

Estoy escribiendo mi propio escritor para archivos XML VTK usando F#. El archivo VTK necesita una codificación base64 de datos binarios, junto con un encabezado como se describe aquí.

... Entonces, los datos binarios deben estar codificados en base64. Además, hay un encabezado antepuesto a los datos; es un entero de 32 bits que contiene la longitud de los datos (en bytes). Este encabezado se codifica por separado. Entonces, en pseudocódigo, la salida de datos se vería así (¡siempre sin espacios ni saltos de línea!)

Mi código se ve así:

let toBase64 (v: int []) =
  use ms = new System.IO.MemoryStream()
  let s = System.Runtime.Serialization.Formatters.Binary.BinaryFormatter()
  s.Serialize(ms, v)
  let b = ms.ToArray()
  let len = System.BitConverter.GetBytes b.Length
  System.Convert.ToBase64String len + System.Convert.ToBase64String b

El resultado, sin embargo, no es correcto. Para la siguiente entrada:

toBase64 [| 1; 2; 3; 4 |]

ParaView (escrito en C++) muestra rangos de -256 a 511 en lugar de 1 a 4. ¿Ve algún error obvio en mi código?

Editar: Mis datos codificados en base64 se ven así:

LAAAAAABAAAA/////wEAAAAAAAAADwEAAAAEAAAACAEAAAACAAAAAwAAAAQAAAAL

EDIT 2: Adjunto mi solución que toma una matriz de flotadores y la convierte en una matriz de bytes, la comprimo usando Ionic.Zip.dll ZLib y la convierto en una cadena base64. La función funciona bien con el archivo XML VTK.

let toZlib (v: float []) =
  use msSinkCompressed = new System.IO.MemoryStream()
  let zOut = new ZlibStream(msSinkCompressed, CompressionMode.Compress, CompressionLevel.Default, true)
  for i in v do 
    let bytes = System.BitConverter.GetBytes i
    zOut.Write(bytes, 0, bytes.Length)
  zOut.Flush()
  zOut.Close()
  let comprBytes = msSinkCompressed.ToArray()
  let header =
    let blocks = System.BitConverter.GetBytes 1
    let len = System.BitConverter.GetBytes (v.Length * 8)
    let comprLen = System.BitConverter.GetBytes comprBytes.Length
    Array.append(Array.append (Array.append blocks len) len) comprLen
  System.Convert.ToBase64String header + System.Convert.ToBase64String comprBytes

preguntado el 22 de noviembre de 10 a las 09:11

2 Respuestas

No sé nada sobre el formato VTK, pero me sorprendería mucho si BinaryFormatter era el camino a seguir: creo que genera una codificación patentada de tipos .NET e incluye mucha información adicional (que el formato VTK no necesita, a menos que se base en la serialización binaria de .NET).

Creo que escribir los valores enteros directamente en la transmisión sería una mejor idea. Como dije, no conozco el formato, pero espero que algo como esto se acerque más a lo que necesitas:

let toBase64 (v: int []) = 
  use ms = new System.IO.MemoryStream() 
  for i in v do 
    let bytes = System.BitConverter.GetBytes i
    ms.Write(bytes, 0, bytes.Length)
  let b = ms.ToArray() 
  let len = System.BitConverter.GetBytes b.Length 
  System.Convert.ToBase64String len + System.Convert.ToBase64String b 

respondido 22 nov., 10:12

¡Gracias, esto funciona bien! ¿Conoces zlib? Me gustaría tomar los datos, zlib y base64 y guardarlos en XML. ¿Puedo usar GZipStream para eso? - Oldrich Svec

¿Por qué Paraview mostraría del 1 al 4? BinaryFormatter es una bestia compleja que incluye un montón de metadatos adicionales sobre los tipos de .NET. Si puede aclarar cómo quiere que se codifique, tal vez pueda ayudar. Por ejemplo, si su 1-4 son bytes debería poder codificarlos en base-64 directamente. Si se van a tratar como números enteros, primero deberá codificarlos en bytes.

En definitiva, sin un detallado regla de codificación (incluido endianness, etc.) es difícil dar una respuesta definitiva, pero BinaryFormatter casi seguro que no.

Estoy muy familiarizado con la codificación binaria personalizada y estoy feliz de ofrecer más ayuda/orientación, pero realmente necesito más información en términos de cuál es el protocolo. esperando.

Por ejemplo, los servicios administrativos de usando C # (lo siento, mi F#-foo es limitado), y asumiendo la codificación de enteros de 32 bits de longitud fija little-endian en todo:

static void Main() {
    string s = Encode(new int[] { 1, 2, 3, 4 });
}
static string Encode(int[] data) {
    byte[] buffer = new byte[data.Length * 4];
    int offset = 0;
    for(int i = 0 ; i < data.Length ; i++) {
        int value = data[i];
        buffer[offset++] = (byte)value;
        buffer[offset++] = (byte)(value >> 8);
        buffer[offset++] = (byte)(value >> 16);
        buffer[offset++] = (byte)(value >> 24);
    }
    byte[] header = new byte[4];
    int len = buffer.Length;
    header[0] = (byte)len;
    header[1] = (byte)(len >> 8);
    header[2] = (byte)(len >> 16);
    header[3] = (byte)(len >> 24);
    return Convert.ToBase64String(header) + Convert.ToBase64String(buffer);
}

respondido 22 nov., 10:12

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.