конвертувати потокові буфери в utf8-string


183

Я хочу зробити HTTP-запит, використовуючи node.js для завантаження тексту з веб-сервера. Оскільки відповідь може містити багато тексту (деякі мегабайти), я хочу обробити кожен фрагмент тексту окремо. Я можу досягти цього за допомогою наступного коду:

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

Здається, це працює без проблем. Однак я хочу підтримувати HTTP-компресію, тому використовую 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
});

Це може бути проблемою для багатобайтових символів, таких як '\u00c4'два байти: 0xC3і 0x84. Якщо перший байт охоплений першим фрагментом ( Buffer), а другий байт - другим фрагментом, то chunk.toString('utf8')в кінці / на початку текстового фрагмента буде створено неправильні символи. Як я можу цього уникнути?

Підказка: мені ще потрібен буфер (точніше кількість байтів у буфері), щоб обмежити кількість завантажених байтів. Тому використання, res.setEncoding('utf8')як у першому прикладі вище коду для нестиснених даних, не відповідає моїм потребам.

Відповіді:


289

Один буфер

Якщо у вас є сингл, Bufferви можете використовувати його toStringметод, який перетворить весь або частину бінарного вмісту в рядок за допомогою певного кодування. Він за замовчуванням застосовується, utf8якщо ви не вказали параметр, але я чітко встановив кодування в цьому прикладі.

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

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

Потокові буфери

Якщо у вас потокові буфери, як у вищезазначеному запитанні, де перший байт багатобайтового UTF8-характера може міститись у першому Buffer(фрагменті), а другий байт у другому, Bufferтоді ви повинні використовувати 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
    });
});

Таким чином байти неповних символів буферуються до тих StringDecoderпір, поки всі необхідні байти не будуть записані в декодер.


63
Ви також можете chunk.toString ('utf8');
— Зугвалт

1
Будь ласка, додайте вищезгадану пропозицію у свою відповідь як оновлення на користь інших. Велике дякую !
— FacePalm

9
@joshperry: sry, але, як пояснює мій текст запитання: chunk.toString('utf8')не завжди працює через багатобайтові символи в UTF8. Я не розумію, чому ви змінили мою відповідь, яка явно подолала цю проблему, використовуючи a StringDecoder. Я щось тут сумую? Має nodeщо - щось змінилося?
— Biggie

8
Я змінив тему-заголовок і відредагував відповідь. Тепер він показує обидва рішення: перетворення потокових буферів та одного буфера за допомогою toString.
— Біггі

1
Дякуємо, що показали, як правильно впоратися з ситуацією, коли багатобайтові символи розділені на шматки. Багато інших ресурсів в Інтернеті цілком ігнорують це, що призводить до баггі-коду, який часто не вийде з ладу, поки не з'явиться у виробництві.
— 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);
});
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.