convertir les tampons streamés en utf8-string


183

Je veux faire une requête HTTP en utilisant node.js pour charger du texte à partir d'un serveur Web. Puisque la réponse peut contenir beaucoup de texte (quelques mégaoctets), je souhaite traiter chaque bloc de texte séparément. Je peux y parvenir en utilisant le code suivant:

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

Cela semble fonctionner sans problème. Cependant, je souhaite prendre en charge la compression HTTP, j'utilise donc 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
});

Cela peut être un problème pour les caractères multi-octets comme '\u00c4'qui se compose de deux octets: 0xC3et 0x84. Si le premier octet est couvert par le premier bloc ( Buffer) et le deuxième octet par le deuxième bloc, alors chunk.toString('utf8')produira des caractères incorrects à la fin / au début du bloc de texte. Comment puis-je éviter ça?

Astuce: j'ai toujours besoin du tampon (plus précisément du nombre d'octets dans le tampon) pour limiter le nombre d'octets téléchargés. Donc, utiliser res.setEncoding('utf8')comme dans le premier exemple de code ci-dessus pour les données non compressées ne répond pas à mes besoins.

Réponses:


289

Tampon unique

Si vous en avez un, Buffervous pouvez utiliser sa toStringméthode qui convertira tout ou partie du contenu binaire en une chaîne en utilisant un encodage spécifique. La valeur par défaut est utf8si vous ne fournissez pas de paramètre, mais j'ai explicitement défini l'encodage dans cet exemple.

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

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

Tampons en continu

Si vous avez diffusé des tampons comme dans la question ci-dessus où le premier octet d'un caractère multi-octets UTF8peut être contenu dans le premier Buffer(bloc) et le deuxième octet dans le second, Buffervous devez utiliser un 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
    });
});

De cette façon, les octets de caractères incomplets sont mis en mémoire tampon par le StringDecoderjusqu'à ce que tous les octets requis aient été écrits dans le décodeur.


63
Vous pouvez également chunk.toString ('utf8');
— Zugwalt

1
Veuillez ajouter la suggestion ci-dessus dans votre réponse en tant que mise à jour pour le bénéfice des autres. Merci beaucoup !
— FacePalm

9
@joshperry: sry, mais comme l'explique ma question-text: chunk.toString('utf8')ne fonctionne pas toujours à cause des caractères multi-octets en UTF8. Je ne comprends pas pourquoi vous avez changé ma réponse qui surmonte explicitement ce problème en utilisant un StringDecoder. Est-ce que je rate quelque chose ici? A nodechangé quelque chose?
— Biggie

8
J'ai changé le titre du sujet et édité la réponse. Il montre maintenant les deux solutions: la conversion des tampons streamés et un seul tampon en utilisant toString.
— Biggie

1
Merci d'avoir montré comment gérer correctement la situation dans laquelle des caractères multi-octets sont divisés en morceaux. De nombreuses autres ressources sur Internet ignorent complètement cela, ce qui conduit à un code bogué qui n'échouera souvent pas tant qu'il n'est pas en production.
— 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);
});
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.