Konvertieren Sie gestreamte Puffer in utf8-string


183

Ich möchte eine HTTP-Anfrage mit node.js stellen, um Text von einem Webserver zu laden. Da die Antwort viel Text (einige Megabyte) enthalten kann, möchte ich jeden Textblock separat verarbeiten. Ich kann dies mit dem folgenden Code erreichen:

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

Dies scheint ohne Probleme zu funktionieren. Ich möchte jedoch die HTTP-Komprimierung unterstützen, daher verwende ich 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
});

Dies kann ein Problem für Multi-Byte-Zeichen sein, '\u00c4'die aus zwei Bytes bestehen: 0xC3und 0x84. Wenn das erste Byte vom ersten Block ( Buffer) und das zweite Byte vom zweiten Block abgedeckt chunk.toString('utf8')wird, werden am Ende / Anfang des Textblocks falsche Zeichen erzeugt. Wie kann ich das vermeiden?

Hinweis: Ich benötige immer noch den Puffer (genauer gesagt die Anzahl der Bytes im Puffer), um die Anzahl der heruntergeladenen Bytes zu begrenzen. Die Verwendung res.setEncoding('utf8')des Codes wie im ersten Beispiel oben für nicht komprimierte Daten entspricht also nicht meinen Anforderungen.

Antworten:


289

Einzelner Puffer

Wenn Sie eine einzelne haben Buffer, können Sie deren toStringMethode verwenden, mit der der gesamte oder ein Teil des binären Inhalts mithilfe einer bestimmten Codierung in eine Zeichenfolge konvertiert wird. Der Standardwert ist, utf8wenn Sie keinen Parameter angeben, aber ich habe die Codierung in diesem Beispiel explizit festgelegt.

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

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

Gestreamte Puffer

Wenn Sie Puffer wie in der obigen Frage gestreamt haben, in denen das erste Byte eines Multi-Byte- UTF8Zeichens im ersten Buffer(Chunk) und das zweite Byte im zweiten enthalten sein Bufferkann, sollten Sie a verwenden 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
    });
});

Auf diese Weise werden Bytes unvollständiger Zeichen von gepuffert, StringDecoderbis alle erforderlichen Bytes in den Decoder geschrieben wurden.


63
Sie können auch zu chunk.toString ('utf8');
Zugwalt

1
Bitte fügen Sie den obigen Vorschlag in Ihrer Antwort als Update zum Nutzen anderer hinzu. Danke vielmals !
FacePalm

9
@joshperry: sry, aber wie mein Fragetext erklärt: chunk.toString('utf8')funktioniert nicht immer wegen Multi-Byte-Zeichen in UTF8. Ich verstehe nicht, warum Sie meine Antwort geändert haben, die dieses Problem explizit durch die Verwendung von a überwindet StringDecoder. Vermisse ich hier etwas Hat nodesich etwas geändert?
Biggie

8
Ich habe den Titel des Themas geändert und die Antwort bearbeitet. Es werden nun beide Lösungen angezeigt: Konvertieren von gestreamten Puffern und eines einzelnen Puffers mit toString.
Biggie

1
Vielen Dank, dass Sie gezeigt haben, wie Sie mit der Situation umgehen können, in der Multi-Byte-Zeichen auf mehrere Blöcke aufgeteilt sind. Viele andere Ressourcen im Internet ignorieren dies vollständig, was zu fehlerhaftem Code führt, der oft erst dann fehlschlägt, wenn er in Produktion ist.
29.

-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);
});
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.