Сериализировать массив int в двоичную / base64 форму для использования в VTK - PullRequest
0 голосов
/ 22 ноября 2010

Я пишу свой собственный писатель для файлов VTK XML, используя F #. Файл VTK нуждается в кодировке двоичных данных base64 вместе с заголовком, как описано здесь .

... Таким образом, двоичные данные должны быть закодированы в base64. Кроме того, к данным добавлен заголовок; это 32-разрядное целое число, содержащее длину данных (в байтах). Этот заголовок кодируется отдельно. Таким образом, в псевдокоде вывод данных будет выглядеть так (всегда без пробелов и переносов строк!)

Мой код выглядит так:

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

Результат, однако, неверен. Для следующего ввода:

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

ParaView (написанный на C ++) отображает диапазоны от -256 до 511 вместо 1 к 4. Видите ли вы какую-либо очевидную ошибку в моем коде?

Редактировать : Мои закодированные в base64 данные выглядят так:

LAAAAAABAAAA ///// wEAAAAAAAAADwEAAAAEAAAACAEAAAACAAAAAwAAAAQAAAAL

EDIT 2 : Я прилагаю свое решение, которое принимает массив с плавающей точкой и преобразует его в байтовый массив, сжимает его с помощью Ionic.Zip.dll ZLib и преобразует в строку base64. Функция отлично работает с файлом VTK XML.

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

Ответы [ 2 ]

1 голос
/ 22 ноября 2010

Я ничего не знаю о формате VTK, но я был бы очень удивлен, если бы BinaryFormatter был таким способом - я думаю, что он генерирует некоторую проприетарную кодировку типов .NET и содержит много дополнительной информации (что формат VTK не нужен, если он не основан на двоичной сериализации .NET).

Я думаю, что запись целочисленных значений непосредственно в поток была бы лучшей идеей.Как я уже сказал, я не знаю формат, но я ожидаю, что что-то вроде этого будет ближе к тому, что вам нужно:

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 
0 голосов
/ 22 ноября 2010

Почему Paraview отображает от 1 до 4?BinaryFormatter - сложный зверь, включающий лоты дополнительных метаданных о типах .NET.Если вы можете уточнить, как вы хотите, чтобы это закодировано, это может помочь.Например, если ваши 1-4 равны байтов , вы сможете напрямую кодировать их в base-64.Если они должны рассматриваться как целые числа, вам нужно сначала закодировать их в байты.

В конечном счете, без подробного правила кодирования (включая порядковый номер и т. Д.) Трудно датьокончательный ответ, но BinaryFormatter почти наверняка не так.

Я очень хорошо знаком с пользовательским двоичным кодированием, и я рад предложить больше помощи / руководства, но мне действительно нужно больше информации в терминахо том, что протокол ожидает .

Например, с использованием C # (извините, мой F # -foo ограничен), и предполагая, что переменная фиксированной длины с прямым порядком байтов 32-битное целочисленное кодирование:

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);
}
...