akışlı arabellekleri utf8-string'e dönüştür


183

Bir web sunucusundan bazı metin yüklemek için node.js kullanarak bir HTTP isteği yapmak istiyorum. Yanıt çok fazla metin içerebileceğinden (bazı Megabayt) Her metin yığınını ayrı ayrı işlemek istiyorum. Aşağıdaki kodu kullanarak bunu elde edebilirsiniz:

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

Bu sorunsuz çalışıyor gibi görünüyor. Ancak HTTP sıkıştırmasını desteklemek istiyorum, bu yüzden zlib kullanıyorum:

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
});

Bu, '\u00c4'iki bayttan oluşan çok baytlı karakterler için bir sorun olabilir : 0xC3ve 0x84. İlk bayt birinci yığın ( Buffer) ve ikinci bayt ikinci yığın tarafından kaplanıyorsa chunk.toString('utf8'), metin yığınının sonunda / başında yanlış karakterler oluşturur. Bundan nasıl kaçınabilirim?

İpucu: İndirilen bayt sayısını sınırlamak için yine de arabelleğe (daha özel olarak tampondaki bayt sayısı) ihtiyacım var. Yani res.setEncoding('utf8')sıkıştırılmamış veriler için yukarıdaki ilk örnek kodda olduğu gibi kullanmak benim ihtiyaçlarına uygun değil.

Yanıtlar:


289

Tek Tampon

Tek bir Buffervarsa toString, ikili içeriğin tamamını veya bir kısmını belirli bir kodlama kullanarak bir dizeye dönüştürecek yöntemini kullanabilirsiniz . utf8Bir parametre belirtmezseniz varsayılan olarak kullanılır , ancak bu örnekte kodlamayı açıkça ayarladım.

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

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

Akışlı Tamponlar

Yukarıdaki sorudaki gibi, çok baytlı bir UTF8karakterin ilk baytının birinci Buffer(yığın) ve ikinci baytın ikincisinde bulunabileceği gibi tamponlar yayınladıysanız, Buffera kullanmalısınız 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
    });
});

Bu şekilde, tamamlanmamış karakterlerin baytları, StringDecodertüm gerekli baytlar dekodere yazılana kadar arabelleğe alınır .


63
Ayrıca chunk.toString ('utf8');
Zugwalt

1
Lütfen yukarıdaki öneriyi başkalarının yararına bir güncelleme olarak yanıtınıza ekleyin. Çok teşekkürler !
FacePalm

9
@joshperry: sry, ancak soru metnimin açıkladığı gibi: UTF8'deki chunk.toString('utf8')çok baytlı karakterler nedeniyle her zaman çalışmaz. A kullanarak bu sorunun üstesinden gelen cevabımı neden değiştirdiğinizi anlamıyorum StringDecoder. Burada bir şey mi özlüyorum? Bir nodeşey değişti mi?
Biggie

8
Konu başlığını değiştirdim ve cevabı düzenledim. Şimdi her iki çözümü de göstermektedir: akışlı tamponları dönüştürme ve tek bir tampon kullanarak toString.
Biggie

1
Çok baytlık karakterlerin parçalar arasında bölündüğü durumla nasıl başa çıkacağınızı gösterdiğiniz için teşekkür ederiz. İnternetteki diğer birçok kaynak bunu tamamen görmezden gelir, bu da üretimde olana kadar başarısız olmayacak olan buggy koduna yol açar.
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);
});
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.