convertir buffers transmitidos a utf8-string


183

Quiero hacer una solicitud HTTP usando node.js para cargar texto de un servidor web. Como la respuesta puede contener mucho texto (algunos Megabytes), quiero procesar cada fragmento de texto por separado. Puedo lograr esto usando el siguiente código:

var req = http.request(reqOptions, function(res) {
    ...
    res.setEncoding('utf8');
    res.on('data', function(textChunk) {
        // process utf8 text chunk
    });
});

Esto parece funcionar sin problemas. Sin embargo, quiero admitir la compresión HTTP, por lo que uso zlib:

var zip = zlib.createUnzip();

// NO res.setEncoding('utf8') here since we need the raw bytes for zlib
res.on('data', function(chunk) {
    // do something like checking the number of bytes downloaded
    zip.write(chunk); // give the raw bytes to zlib, s.b.
});

zip.on('data', function(chunk) {
    // convert chunk to utf8 text:
    var textChunk = chunk.toString('utf8');

    // process utf8 text chunk
});

Esto puede ser un problema para los caracteres de varios bytes como el '\u00c4'que consta de dos bytes: 0xC3y 0x84. Si el primer byte está cubierto por el primer fragmento ( Buffer) y el segundo byte por el segundo fragmento chunk.toString('utf8')producirá caracteres incorrectos al final / principio del fragmento de texto. ¿Cómo puedo evitar esto?

Sugerencia: todavía necesito el búfer (más específicamente el número de bytes en el búfer) para limitar el número de bytes descargados. Por lo tanto, usar res.setEncoding('utf8')como en el primer código de ejemplo anterior para datos no comprimidos no se ajusta a mis necesidades.

Respuestas:


289

Tampón individual

Si tiene uno solo Buffer, puede usar su toStringmétodo que convertirá todo o parte del contenido binario en una cadena usando una codificación específica. Su valor predeterminado es utf8si no proporciona un parámetro, pero he establecido explícitamente la codificación en este ejemplo.

var req = http.request(reqOptions, function(res) {
    ...

    res.on('data', function(chunk) {
        var textChunk = chunk.toString('utf8');
        // process utf8 text chunk
    });
});

Buffers fluidos

Si ha transmitido búferes como en la pregunta anterior, donde el primer byte de un carácter de varios bytes UTF8puede estar contenido en el primer Buffer(fragmento) y el segundo byte en el segundo, Bufferentonces debe usar a StringDecoder. :

var StringDecoder = require('string_decoder').StringDecoder;

var req = http.request(reqOptions, function(res) {
    ...
    var decoder = new StringDecoder('utf8');

    res.on('data', function(chunk) {
        var textChunk = decoder.write(chunk);
        // process utf8 text chunk
    });
});

De esta forma, los bytes de caracteres incompletos se almacenan en el búfer StringDecoderhasta que se hayan escrito todos los bytes necesarios en el decodificador.


63
También puede chunk.toString ('utf8');
— Zugwalt

1
Agregue la sugerencia anterior en su respuesta como una actualización en beneficio de otros. Muchas gracias !
— FacePalm

9
@joshperry: sry, pero como explica mi texto de pregunta: chunk.toString('utf8')no siempre funciona debido a los caracteres de varios bytes en UTF8. No entiendo por qué cambiaste mi respuesta que explícitamente resolvió este problema usando a StringDecoder. ¿Echo de menos algo aquí? Ha nodecambiado algo?
— Biggie

8
Cambié el título del tema y edité la respuesta. Ahora muestra ambas soluciones: convertir búferes transmitidos y un solo búfer usando toString.
— Biggie

1
Gracias por mostrar cómo lidiar adecuadamente con la situación en la que los caracteres de varios bytes se dividen en fragmentos. Muchos otros recursos en Internet ignoran esto por completo, lo que conduce a un código defectuoso que a menudo no fallará hasta que esté en producción.
— jlh

-4
var fs = require("fs");

function readFileLineByLine(filename, processline) {
    var stream = fs.createReadStream(filename);
    var s = "";
    stream.on("data", function(data) {
        s += data.toString('utf8');
        var lines = s.split("\n");
        for (var i = 0; i < lines.length - 1; i++)
            processline(lines[i]);
        s = lines[lines.length - 1];
    });

    stream.on("end",function() {
        var lines = s.split("\n");
        for (var i = 0; i < lines.length; i++)
            processline(lines[i]);
    });
}

var linenumber = 0;
readFileLineByLine(filename, function(line) {
    console.log(++linenumber + " -- " + line);
});
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.