конвертировать потоковые буферы в строку utf8


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тогда вы должны использовать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');
Zugwalt

1
Пожалуйста, добавьте вышеуказанное предложение в свой ответ в качестве обновления для пользы других. Большое спасибо !
FacePalm

9
@joshperry: sry, но, как объясняет мой вопрос-текст: chunk.toString('utf8')не всегда работает из-за многобайтовых символов в UTF8. Я не понимаю, почему вы изменили мой ответ, который явно преодолел эту проблему с помощью StringDecoder. Я что-то здесь скучаю? Что- nodeто изменилось?
Biggie

8
Я изменил название темы и отредактировал ответ. Теперь показаны оба решения: преобразование потоковых буферов и использование одного буфера toString.
Biggie

1
Спасибо за показ того, как правильно справиться с ситуацией, когда многобайтовые символы разделены на куски. Многие другие ресурсы в Интернете полностью игнорируют это, что приводит к ошибочному коду, который часто не будет работать до тех пор, пока он не будет запущен в производство.
июля

-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.