konwertuje buforowane strumienie na utf8-string


183

Chcę złożyć żądanie HTTP za pomocą node.js, aby załadować tekst z serwera WWW. Ponieważ odpowiedź może zawierać dużo tekstu (niektóre megabajty), chcę przetwarzać każdy fragment tekstu osobno. Mogę to osiągnąć za pomocą następującego kodu:

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

To wydaje się działać bez problemów. Jednak chcę obsługiwać kompresję HTTP, więc używam 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
});

Może to stanowić problem dla znaków wielobajtowych, takich jak '\u00c4'który składa się z dwóch bajtów: 0xC3i 0x84. Jeśli pierwszy bajt jest objęty pierwszą porcją ( Buffer), a drugi bajt drugą porcją, wówczas chunk.toString('utf8')na końcu / początku fragmentu tekstu pojawią się niepoprawne znaki. Jak mogę tego uniknąć?

Wskazówka: Nadal potrzebuję bufora (a dokładniej liczby bajtów w buforze), aby ograniczyć liczbę pobranych bajtów. Zatem użycie res.setEncoding('utf8')kodu jak w pierwszym przykładzie powyżej dla nieskompresowanych danych nie odpowiada moim potrzebom.

Odpowiedzi:


289

Pojedynczy bufor

Jeśli masz taki Buffer, możesz użyć jego toStringmetody, która przekonwertuje całość lub część zawartości binarnej na ciąg znaków przy użyciu określonego kodowania. Domyślnie jest to ustawione, utf8jeśli nie podasz parametru, ale jawnie ustawiłem kodowanie w tym przykładzie.

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

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

Bufory przesyłane strumieniowo

Jeśli masz streamowane bufory, jak w powyższym pytaniu, gdzie pierwszy bajt wielobajtowego UTF8znaku może być zawarty w pierwszym Buffer(porcji), a drugi w drugim, Bufferto powinieneś użyć 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
    });
});

W ten sposób bajty niekompletnych znaków są buforowane do StringDecodermomentu, aż wszystkie wymagane bajty zostaną zapisane w dekoderze.


63
Możesz także do chunk.toString ('utf8');
Zugwalt,

1
Dodaj powyższą sugestię do swojej odpowiedzi jako aktualizację z korzyścią dla innych. Wielkie dzięki !
FacePalm

9
@joshperry: sry, ale jak wyjaśnia mój tekst pytania: chunk.toString('utf8')nie zawsze działa z powodu wielobajtowych znaków w UTF8. Nie rozumiem, dlaczego zmieniłeś moją odpowiedź, która wyraźnie rozwiązuje ten problem za pomocą StringDecoder. Czy coś tu brakuje? Czy nodecoś się zmieniło?
Biggie,

8
Zmieniłem tytuł tematu i zredagowałem odpowiedź. Teraz pokazuje oba rozwiązania: konwersję buforów strumieniowych i pojedynczego bufora za pomocą toString.
Biggie,

1
Dziękujemy za pokazanie, jak właściwie radzić sobie z sytuacją, w której znaki wielobajtowe są podzielone na części. Wiele innych zasobów w Internecie całkowicie to ignoruje, co prowadzi do błędów w kodzie, które często nie zawodzą, dopóki nie zostaną uruchomione.
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);
});
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.