chuyển đổi bộ đệm truyền phát thành chuỗi utf8


183

Tôi muốn tạo một yêu cầu HTTP bằng cách sử dụng node.js để tải một số văn bản từ máy chủ web. Vì phản hồi có thể chứa nhiều văn bản (một số Megabyte) nên tôi muốn xử lý từng đoạn văn bản riêng biệt. Tôi có thể đạt được điều này bằng cách sử dụng mã sau đây:

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

Điều này dường như làm việc mà không có vấn đề. Tuy nhiên tôi muốn hỗ trợ nén HTTP, vì vậy tôi sử dụng 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
});

Đây có thể là một vấn đề đối với các ký tự nhiều byte như '\u00c4'bao gồm hai byte: 0xC30x84. Nếu byte đầu tiên được bao phủ bởi đoạn đầu tiên ( Buffer) và byte thứ hai bởi đoạn thứ hai thì chunk.toString('utf8')sẽ tạo ra các ký tự không chính xác ở cuối / đầu đoạn văn bản. Làm thế nào tôi có thể tránh điều này?

Gợi ý: Tôi vẫn cần bộ đệm (cụ thể hơn là số byte trong bộ đệm) để giới hạn số byte được tải xuống. Vì vậy, sử dụng res.setEncoding('utf8')like trong mã ví dụ đầu tiên ở trên cho dữ liệu không nén không phù hợp với nhu cầu của tôi.

Câu trả lời:


289

Bộ đệm đơn

Nếu bạn có một đơn, Bufferbạn có thể sử dụng toStringphương thức của nó để chuyển đổi tất cả hoặc một phần nội dung nhị phân thành một chuỗi bằng cách sử dụng một mã hóa cụ thể. Nó mặc định là utf8nếu bạn không cung cấp một tham số, nhưng tôi đã đặt mã hóa rõ ràng trong ví dụ này.

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

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

Bộ đệm phát trực tuyến

Nếu bạn đã phát trực tuyến bộ đệm như trong câu hỏi ở trên, nơi byte đầu tiên của đa byte UTF8có thể được chứa trong byte đầu tiên Buffer(chunk) và byte thứ hai trong giây Bufferthì bạn nên sử dụng 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
    });
});

Bằng cách này, các byte của các ký tự không hoàn chỉnh được đệm bởi StringDecodercho đến khi tất cả các byte cần thiết được ghi vào bộ giải mã.


63
Bạn cũng có thể chunk.toString ('utf8');
Zugwalt

1
Vui lòng thêm đề xuất ở trên trong câu trả lời của bạn dưới dạng cập nhật vì lợi ích của người khác. Cảm ơn nhiều !
FacePalm

9
@joshperry: sry, nhưng như văn bản câu hỏi của tôi giải thích: chunk.toString('utf8')không phải lúc nào cũng hoạt động vì các ký tự nhiều byte trong UTF8. Tôi không hiểu tại sao bạn thay đổi câu trả lời của tôi mà khắc phục rõ ràng vấn đề này bằng cách sử dụng a StringDecoder. Tôi có bỏ lỡ điều gì ở đây không? Đã nodethay đổi điều gì?
Biggie

8
Tôi đã thay đổi tiêu đề chủ đề và chỉnh sửa câu trả lời. Bây giờ nó hiển thị cả hai giải pháp: chuyển đổi bộ đệm truyền phát và bộ đệm đơn bằng cách sử dụng toString.
Biggie

1
Cảm ơn đã chỉ ra cách xử lý chính xác tình huống trong đó các ký tự nhiều byte được chia thành nhiều phần. Nhiều tài nguyên khác trên internet bỏ qua điều này hoàn toàn, điều này dẫn đến mã lỗi thường không bị lỗi cho đến khi nó được sản xuất.
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);
});
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.