深入浅出Node.js読書ノート:Bufferのつなぎ合わせ(6.3)
18009 ワード
Bufferの使用では、通常、入力ストリームでコンテンツを読み出すコードとして、一定の転送が行われます.
上記のコードは読み出しストリームのモデルコードであり,初心者にとっては問題はない.しかし、よく考えてみると、ストリームにワイドバイト符号化がある場合、問題が発生します.問題はdata+=chunkです.このコードには、次のコードに等しいtoString()操作が隠されています.
ワイドバイトの中国語では、問題があります.
この問題をシミュレートするために、ファイルストリームからBufferを読み込み、Bufferの長さを11に制限します.
コードは次のとおりです.
test.mdデータは李白の《静夜思》で、プログラムを実行して、以下の出力を得ることができます:ベッドの前は明るく???光;疑い???地上の霜首を挙げる??明月、???故郷に思いを馳せる.
詩には複数の疑問符が現れ、発生の原因を分析した.
Bufferの長さが11に制限されているため、ストリームから7回読み取ってからすべてのデータの読み取りが完了し、結果は以下の通りである.
…
前述のBufferのデフォルトの符号化はutf-8であり、中国語バイトutf-8の符号化では3バイトを占めているため、1回目のBuffer読み取りが完了すると3つの中国語が表示され、残りの2バイト(e 6 9 c)には乱符号(疑問符)が表示される.
2番目のBufferオブジェクトの1バイト目も文字化できず、文字化けしてしか表示できません.この例では、Bufferを11バイトに制限することで疑問符が発生し、Bufferバイトの長さを緩和すると、この問題は依然として発生します.
ストリームを読み込むときに、ストリームの符号化方式を設定できます.サンプルコードは次のとおりです.
改善されたコードは次のとおりです.
プログラムを再実行した後、正しい中国語を出力したことに驚きました.
setEncoding()を呼び出すと、読み取り可能なストリームオブジェクトにdecoderオブジェクトが内部に設定されます.このオブジェクトはstring_からdecoderモジュールStringDecoderインスタンスオブジェクト.
以下、コードで具体的に説明する.
以前の2つのBufferオブジェクトをdecoderに書き込むと、不思議なことに「月」のトランスコードが通常のように2つの部分で別々に出力されていないことがわかります.StringDecoderは、符号化された後、ワイドバイト文字列がutf-8符号化で3バイトで格納されていることを知り、最初のwrite()の場合、9バイトのトランスコードで形成された文字しか出力しない.2回目のwriteでは,残り2バイトと11バイトを一括して3バイトトランスコードする.
文字列を正しくつなぎ合わせるにはどうすればいいですか?コードは次のとおりです.
正確な接合方法は、全てのBufferフラグメントを受信し、全てのフラグメントの全長を記録した後、Bufferを呼び出す配列である.concat()メソッドは、結合されたBufferオブジェクトを生成します.Buffer.concat()メソッドは、Bufferオブジェクトから大きなBufferオブジェクトへのレプリケーションプロセスをカプセル化します.
var fs = require('fs');
var rs = fs.createReadStream('test.md');
var data = '';
rs.on("data", function (chunk){
data += chunk;
});
rs.on("end", function () {
console.log(data);
});
上記のコードは読み出しストリームのモデルコードであり,初心者にとっては問題はない.しかし、よく考えてみると、ストリームにワイドバイト符号化がある場合、問題が発生します.問題はdata+=chunkです.このコードには、次のコードに等しいtoString()操作が隠されています.
data = data.toString() + chunk.toString();
ワイドバイトの中国語では、問題があります.
この問題をシミュレートするために、ファイルストリームからBufferを読み込み、Bufferの長さを11に制限します.
コードは次のとおりです.
var rs = fs.createReadStream('test.md', {
highWaterMark: 11});
test.mdデータは李白の《静夜思》で、プログラムを実行して、以下の出力を得ることができます:ベッドの前は明るく???光;疑い???地上の霜首を挙げる??明月、???故郷に思いを馳せる.
6.3.1文字化けしはどのように発生したのか
詩には複数の疑問符が現れ、発生の原因を分析した.
Bufferの長さが11に制限されているため、ストリームから7回読み取ってからすべてのデータの読み取りが完了し、結果は以下の通りである.
…
前述のBufferのデフォルトの符号化はutf-8であり、中国語バイトutf-8の符号化では3バイトを占めているため、1回目のBuffer読み取りが完了すると3つの中国語が表示され、残りの2バイト(e 6 9 c)には乱符号(疑問符)が表示される.
2番目のBufferオブジェクトの1バイト目も文字化できず、文字化けしてしか表示できません.この例では、Bufferを11バイトに制限することで疑問符が発生し、Bufferバイトの長さを緩和すると、この問題は依然として発生します.
6.3.2 setEncoding()とstring_decoder()
ストリームを読み込むときに、ストリームの符号化方式を設定できます.サンプルコードは次のとおりです.
readable.setEncoding(encoding)
改善されたコードは次のとおりです.
var rs = fs.createReadStream('test.md', {
highWaterMark: 11});
rs.setEncoding('utf8');
プログラムを再実行した後、正しい中国語を出力したことに驚きました.
setEncoding()を呼び出すと、読み取り可能なストリームオブジェクトにdecoderオブジェクトが内部に設定されます.このオブジェクトはstring_からdecoderモジュールStringDecoderインスタンスオブジェクト.
以下、コードで具体的に説明する.
var StringDecoder = require('string_decoder').StringDecoder;
var decoder = new StringDecoder('utf8');
var buf1 = new Buffer([0xE5, 0xBA, 0x8A, 0xE5, 0x89, 0x8D, 0xE6, 0x98, 0x8E, 0xE6, 0x9C]);
console.log(decoder.write(buf1));
var buf2 = new Buffer([0x88, 0xE5, 0x85, 0x89, 0xEF, 0xBC, 0x8C, 0xE7, 0x96, 0x91, 0xE6]);
console.log(decoder.write(buf2));
以前の2つのBufferオブジェクトをdecoderに書き込むと、不思議なことに「月」のトランスコードが通常のように2つの部分で別々に出力されていないことがわかります.StringDecoderは、符号化された後、ワイドバイト文字列がutf-8符号化で3バイトで格納されていることを知り、最初のwrite()の場合、9バイトのトランスコードで形成された文字しか出力しない.2回目のwriteでは,残り2バイトと11バイトを一括して3バイトトランスコードする.
6.3.3正確な接合Buffer
文字列を正しくつなぎ合わせるにはどうすればいいですか?コードは次のとおりです.
var chunks = [];
var size = 0;
res.on('data', function (chunk) {
chunks.push(chunk);
size += chunk.length;
});
res.on('end', function () {
var buf = Buffer.concat(chunks, size);
var str = iconv.decode(buf, 'utf8');
console.log(str);
});
正確な接合方法は、全てのBufferフラグメントを受信し、全てのフラグメントの全長を記録した後、Bufferを呼び出す配列である.concat()メソッドは、結合されたBufferオブジェクトを生成します.Buffer.concat()メソッドは、Bufferオブジェクトから大きなBufferオブジェクトへのレプリケーションプロセスをカプセル化します.
Buffer.concat = function(list, length) {
if (!Array.isArray(list)) {
throw new Error('Usage: Buffer.concat(list, [length])');
}
if (list.length === 0) {
return new Buffer(0);
} else if (list.length === 1) {
return list[0];
}
if (typeof length !== 'number') {
length = 0;
for (var i = 0; i < list.length; i++) {
var buf = list[i];
length += buf.length;
}
}
var buffer = new Buffer(length);
var pos = 0;
for (var i = 0; i < list.length; i++) {
var buf = list[i];
buf.copy(buffer, pos);
pos += buf.length;
}
return buffer;
};