converte i buffer in streaming in utf8-string


183

Voglio fare una richiesta HTTP usando node.js per caricare del testo da un server web. Poiché la risposta può contenere molto testo (alcuni Megabyte), voglio elaborare ciascun blocco di testo separatamente. Posso ottenere questo usando il seguente codice:

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

Questo sembra funzionare senza problemi. Tuttavia, voglio supportare la compressione HTTP, quindi 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
});

Questo può essere un problema per caratteri multi-byte come il '\u00c4'quale consiste di due byte: 0xC3e 0x84. Se il primo byte è coperto dal primo blocco ( Buffer) e il secondo byte dal secondo blocco, chunk.toString('utf8')verranno generati caratteri errati alla fine / all'inizio del blocco di testo. Come posso evitarlo?

Suggerimento: ho ancora bisogno del buffer (in particolare il numero di byte nel buffer) per limitare il numero di byte scaricati. Quindi usare res.setEncoding('utf8')come nel primo codice di esempio sopra per i dati non compressi non soddisfa le mie esigenze.

Risposte:


289

Buffer singolo

Se ne hai uno singolo Bufferpuoi usare il suo toStringmetodo che convertirà tutto o parte del contenuto binario in una stringa usando una codifica specifica. L'impostazione predefinita è utf8se non si fornisce un parametro, ma in questo esempio ho impostato esplicitamente la codifica.

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

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

Buffer in streaming

Se hai buffer in streaming come nella domanda sopra in cui il primo byte di un UTF8carattere multibyte può essere contenuto nel primo Buffer(blocco) e il secondo byte nel secondo, Bufferallora dovresti usare 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
    });
});

In questo modo byte di caratteri incompleti vengono bufferizzati StringDecoderfino a quando tutti i byte richiesti non vengono scritti nel decodificatore.


63
Puoi anche chunk.toString ('utf8');
Zugwalt,

1
Aggiungi il suggerimento sopra nella tua risposta come aggiornamento a beneficio di altri. Grazie molto !
FacePalm,

9
@joshperry: sry, ma come spiega il mio testo di domanda: chunk.toString('utf8')non sempre funziona a causa dei caratteri multi-byte in UTF8. Non capisco perché hai cambiato la mia risposta che ha superato esplicitamente questo problema usando un StringDecoder. Mi manca qualcosa qui? È nodecambiato qualcosa?
Biggie,

8
Ho cambiato il titolo dell'argomento e modificato la risposta. Ora mostra entrambe le soluzioni: la conversione di buffer in streaming e un singolo buffer mediante toString.
Biggie,

1
Grazie per aver mostrato come gestire correttamente la situazione in cui i caratteri multibyte sono suddivisi in blocchi. Molte altre risorse su Internet lo ignorano completamente, il che porta a un codice errato che spesso non fallirà fino a quando non sarà in produzione.
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);
});
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.