「Node.js入門」勉強会にて、
質問:
勉強会参加者から、
「Webサーバなどで dataイベントで受け取る chunk Buffer を単純に文字列変換しているとマルチバイト文字を受けた時に文字化けする可能性はないですか?」
と質問を受けました。あっ!
var data = '';
socket.on('data', function(chunk) {
data += chunk;
});
を本のサンプルコードで各所に書いた覚えが…
「Node 内部でうまくエンコードの切れ目の処理してるんじゃないかなぁ~ ちゃんと調べて回答します。」
と一時しのぎの答えをしたので、ちゃんと回答します。
回答:
はい。文字化けしちゃいます。以下のサンプルコードの様に、utf8文字エンコードの切れ目でないところでデータを読みだしていると見事文字化けします。
var http = require('http');
var port = 8080;
var server = http.createServer(function(req, res) {
var data = '';
req.on('data', function(chunk) {
data += chunk;
});
req.on('end', function() {
console.log('data:', data);
res.writeHead(200);
res.end();
server.close();
});
});
server.listen(port, function() {
var client = http.request({port: port, method: 'POST'});
var buf = new Buffer('\u3042\u3044\u3046\u3048\u304a'); // 'あいうえお'
client.write(buf.slice(0,8));
client.write(buf.slice(8));
client.end();
});
実行結果
> node buf1.js
data: あい???えお
そうですよね。マルチバイト文字の事はちゃんと最初から考慮に入れておかないといけないです。内部でよろしくやってくれるはずと勝手に思い込んではいけません。
じゃ正解は?
どんなエンコード形式で送られて来るのがわかっていれば、ちゃんとエンコードタイプを指定してあげることです。
下記のサンプルコードでは、HTTPレスポンスボディをutf8で受け取ることを明示的に設定しています。
var http = require('http');
var port = 8080;
var server = http.createServer(function(req, res) {
var data = '';
req.setEncoding('utf8'); // 受信するレスポンスボディのエンコード形式をutf8に指定
req.on('data', function(chunk) {
console.log('chunk:', chunk);
data += chunk;
});
req.on('end', function() {
console.log('data;', data);
res.writeHead(200);
res.end();
server.close();
});
});
server.listen(port, function() {
var client = http.request({port: port, method: 'POST'});
var buf = new Buffer('\u3042\u3044\u3046\u3048\u304a'); // 'あいうえお'
client.write(buf.slice(0,8));
client.write(buf.slice(8));
client.end();
});
出力結果
> node buf2.js
chunk: あい
chunk: うえお
data; あいうえお
おぉ、文字化け解消ですね。受信している chunk が変なところで分割されているのに結果が大丈夫なのは、内部処理でstring_decoder
オブジェクトが生成されていて、半端データを内部に保持しているからなんですね。
バッファ型のままで扱うなら、下のサンプルの様に配列で蓄えておいて最後にBuffer.concat()
でつなげるという技がいいかと思います。
var http = require('http');
var port = 8080;
var server = http.createServer(function(req, res) {
var bufs = []; // バッファを蓄えておく配列
bufs.totalLength = 0; // 受け取ったバッファの合計サイズ
req.on('data', function(chunk) {
bufs.push(chunk);
bufs.totalLength += chunk.length;
});
req.on('end', function() {
var data = Buffer.concat(bufs, bufs.totalLength);
console.log('data:', data.toString());
res.writeHead(200);
res.end();
server.close();
});
});
server.listen(port, function() {
var client = http.request({port: port, method: 'POST'});
var buf = new Buffer('\u3042\u3044\u3046\u3048\u304a'); // 'あいうえお'
client.write(buf.slice(0,8));
client.write(buf.slice(8));
client.end();
});
出力結果
> node buf3.js
data: あいうえお