es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Leyendo una estructura binaria en Javascript

Tengo una tabla que estoy tratando de leer en Javascript, con datos lo suficientemente grandes como para querer tenerlos en formato binario para ahorrar espacio. La mayoría de la tabla son números o enumeraciones, pero hay algunos datos que son cadenas de texto. Estoy tratando de encontrar la mejor manera de leer esos datos en Javascript. ¿Algún consejo?

Por cierto, la versión comprimida en texto tiene ~33 MB, mientras que el archivo binario comprimido tiene ~20 MB, por eso estoy tratando de reducir el tamaño, con la esperanza de que cargue más rápido. Estoy planeando realizar un preprocesamiento de los datos con Python, lo cual reducirá los datos a solo lo que realmente me importa. Puede que esté filtrando algunos de estos datos, pero idealmente me gustaría tener un sitio web estático, por lo que prefiero no depender de ningún código en el servidor. El archivo que tiene casi cualquier cosa importante tiene solo ~1 MB, así que no creo que sea un gran inconveniente.

Puntos extra si el sistema permite un flotante de 16 bits, ya que hay varios flotantes donde 16 bits es más que suficiente precisión para capturar adecuadamente.

Mi código, por simple que sea hasta ahora, es el siguiente. Tenga en cuenta el bloque faltante donde ocurre algo útil…

function loadBinaryFloatArray(url) {

    var mRequest = new XMLHttpRequest();
    mRequest.open('GET', url);
    mRequest.responseType = 'arraybuffer';

    mRequest.onreadystatechange = function () {
        if (this.readyState === 4) {


        }
    };
    mRequest.send();
}

console.log("Test")

loadBinaryFloatArray("/data/sbdb_important.bin")

Los datos se ven algo así. Tenga en cuenta que probablemente eliminaré algunas de estas columnas, pero estos son los datos fuente. Para referencia, solo la primera columna se almacenará como una cadena de texto, los otros se pueden almacenar como números o enumeraciones convertidas a números. Probablemente habrá dos cadenas de texto reales, creo que voy a dividir la cadena en sus dos partes.

Tabla de datos

Para referencia, el código de Python para empacar es el siguiente. Puedo cambiar las ‘e’ por algo más si es necesario, ya sea float32 o un entero que se convierte en decimal, pero sería bueno tener compatibilidad nativa.

struct.pack('ehifeefff', diameter, type, epoch, a, e, i, om, w, ma)
Tags:  , , , ,

Answer

  1. Avatar for davy.ai

    Una posible solución para leer los datos binarios con cadenas de texto en JavaScript es utilizar TypedArrays. Los TypedArrays te permiten leer y escribir datos binarios en un formato específico. Puedes utilizar las API TextEncoder y TextDecoder para convertir entre cadenas de texto y sus representaciones de bytes correspondientes. Aquí tienes un ejemplo de cómo leer datos binarios con cadenas de texto utilizando TypedArrays:

    function loadBinaryData(url) {
      // Crea un objeto XMLHttpRequest para recuperar los datos binarios
      const xhr = new XMLHttpRequest();
      xhr.open('GET', url);
      xhr.responseType = 'arraybuffer';
    
      xhr.onload = function() {
        // Los datos binarios ahora están disponibles como un ArrayBuffer
        const buffer = xhr.response;
        const dataView = new DataView(buffer);
    
        // Lee el número de filas y columnas de los datos binarios
        const numRows = dataView.getUint32(0, true);
        const numCols = dataView.getUint32(4, true);
    
        // Asigna TypedArrays para almacenar los datos
        const numbers = new Float32Array(numRows * (numCols - 1));
        const strings = new Array(numRows);
    
        // Lee los datos del formato binario
        let offset = 8;
        for (let i = 0; i < numRows; i++) {
          // Lee los números
          for (let j = 0; j < numCols - 1; j++) {
            numbers[i * (numCols - 1) + j] = dataView.getFloat32(offset, true);
            offset += 4;
          }
    
          // Lee la cadena como una serie de bytes
          const stringBytes = [];
          while (true) {
            const byte = dataView.getUint8(offset);
            offset += 1;
            if (byte === 0) {
              break;
            }
            stringBytes.push(byte);
          }
    
          // Convierte los bytes de la cadena a una cadena de texto utilizando TextDecoder
          const string = new TextDecoder().decode(new Uint8Array(stringBytes));
          strings[i] = string;
        }
    
        // Ahora tienes arrays de números y cadenas de texto
        console.log(numbers);
        console.log(strings);
      };
    
      xhr.send();
    }
    

    En este ejemplo, se asume que los datos binarios están en un formato específico, con los primeros 8 bytes que contienen el número de filas y columnas como enteros sin signo de 32 bits en orden de bytes little-endian. Se asume que los datos restantes contienen los datos de la tabla en orden mayor-fila, donde todos los datos numéricos se almacenan como valores de coma flotante de 32 bits en orden de bytes little-endian, y todos los datos de cadena se almacenan como una secuencia de bytes UTF-8 terminada en null.

    Para escribir los datos binarios en Python, puedes utilizar el módulo struct para empaquetar los datos en el formato deseado, y luego guardarlo como un archivo binario. Aquí tienes un ejemplo:

    import struct
    
    # Datos de ejemplo
    data = [
      (1.23, 4, b'hello'),
      (5.67, 8, b'world'),
    ]
    
    # Empaqueta los datos en formato binario
    packed_data = struct.pack('<iiff6siff6s', len(data),="" 3,="" *data[0],="" *data[1])="" #="" guarda="" los="" datos="" binarios="" en="" un="" archivo="" with="" open('data.bin',="" 'wb')="" as="" f:="" f.write(packed_data)="" ```="" en="" este="" ejemplo,="" se="" asume="" que="" los="" datos="" consisten="" en="" dos="" filas="" con="" tres="" columnas="" cada="" una.="" se="" asume="" que="" las="" dos="" primeras="" columnas="" son="" valores="" de="" coma="" flotante,="" y="" la="" tercera="" columna="" es="" una="" secuencia="" de="" bytes="" terminada="" en="" null="" de="" longitud="" 6,="" codificada="" como="" ascii.="" la="" función="" `struct.pack`="" se="" utiliza="" para="" empaquetar="" los="" datos="" en="" el="" formato="" deseado,="" utilizando="" el="" prefijo="" `<`="" para="" indicar="" el="" orden="" de="" bytes="" little-endian.="" los="" bytes="" resultantes="" se="" guardan="" en="" un="" archivo="" utilizando="" el="" modo="" `wb`="" para="" indicar="" el="" modo="" binario.="" ten="" en="" cuenta="" que="" si="" deseas="" admitir="" valores="" de="" coma="" flotante="" de="" 16="" bits,="" puedes="" utilizar="" el="" código="" de="" formato="" `f`="" en="" `struct.pack`="" para="" empaquetar="" los="" datos="" como="" valores="" de="" coma="" flotante="" ieee="" de="" 32="" o="" 64="" bits,="" y="" luego="" convertirlos="" a="" valores="" de="" coma="" flotante="" de="" 16="" bits="" utilizando="" javascript.="" aquí="" tienes="" un="" ejemplo:="" ```python="" import="" struct="" #="" datos="" de="" ejemplo="" data="[" (1.23,="" 4,="" b'hello'),="" (5.67,="" 8,="" b'world'),="" ]="" #="" empaqueta="" los="" datos="" en="" formato="" binario="" como="" floats="" de="" 32="" bits="" packed_data=""></iiff6siff6s',><iiffff6siffff6s', len(data),="" 3,="" *data[0],="" *data[1])="" #="" convierte="" los="" floats="" de="" 32="" bits="" a="" floats="" de="" 16="" bits="" en="" javascript="" function="" convertfloat32tofloat16(value)="" {="" const="" sign="(value" <="" 0)="" 32768="" :="" 0;="" const="" abs="Math.abs(value);" if="" (abs="">= Math.pow(2, -14)) {
        if (abs >= 1.0) {
          return sign | 31744; // Infinity or NaN
        }
        const exp = Math.floor(Math.log2(abs)) + 15;
        const frac = (abs / Math.pow(2, exp - 15)) & 2047;
        return sign | (exp << 10) | frac;
      } else {
        return sign;
      }
    }
    
    const numbers = new Uint16Array(data.length * 3);
    const floats = new Float32Array(packed_data);
    for (let i = 0; i < floats.length; i++) {
      numbers[i] = convertFloat32ToFloat16(floats[i]);
    }
    
    // Ahora tienes un array de floats de 16 bits
    console.log(numbers);
    

    En este ejemplo, los datos se empaquetan en formato binario como floats de 32 bits utilizando el código de formato f, y los bytes resultantes se convierten en un Float32Array en JavaScript. Luego, se utiliza la función convertFloat32ToFloat16 para convertir cada float de 32 bits en un float de 16 bits extrayendo su signo, exponente y fracción, y empaquetándolos en un solo entero de 16 bits utilizando el formato de coma flotante de media precisión IEEE. El array resultante de floats de 16 bits se almacena como un Uint16Array.</iiffff6siffff6s’,>

Comments are closed.