Node.jsといえば、UTF-8のエンコーディングが基本で、Shift_JISやEUC-JPなどを扱う場合にはどうするんだろう?と思って
いたのですが、「iconv-lite」というモジュールを使用するのがよいみたいです。
GitHub - ashtuchkin/iconv-lite: Convert character encodings in pure javascript.
これを見ると、変換表を実装している感じなんですねぇ、と…。
https://github.com/ashtuchkin/iconv-lite/tree/v0.4.19/encodings
サポートしているエンコーディングは、こちら。
Supported Encodings · ashtuchkin/iconv-lite Wiki · GitHub
とりあえず、使っていってみましょう。
環境
確認時の環境は、以下の通り。
$ node -v v9.5.0 $ npm -v 5.6.0
準備
ドキュメントに沿って、npmでインストール。
$ npm install --save iconv-lite
package.jsonでの依存関係は、このようになりました。
"dependencies": { "iconv-lite": "^0.4.19" },
動作自体は、テストコードで確認しましょう。
今回は、Jestを使うことにしました。
$ npm install --save-dev jest
package.jsonでの依存関係は、このように。
"devDependencies": { "jest": "^22.2.2" }
「npm run test」で、Jestが起動するようにしておきました。
"scripts": { "test": "jest" },
テストデータも用意しておきましょう。ファイルで、iconvを使って簡単に作ります。
$ echo -n "あいうえお" > data/utf8.txt $ iconv -f UTF-8 -t Windows-31J -o data/sjis.txt data/utf8.txt $ iconv -f UTF-8 -t EUCJP-MS -o data/eucjp.txt data/utf8.txt
こちらを使って確認していきましょう。
確認
まずは、requireから。
test/iconv-lite.test.js
const fs = require("fs"); const iconv = require("iconv-lite");
fsモジュールはファイル読み込みのためで、今回の主役はiconv-liteです。
decode
とりあえず、UTF-8のファイルは標準機能で読んでみます。
test("read standard utf8", done => { fs.readFile("data/utf8.txt", "utf-8", (err, data) => { expect(data).toEqual("あいうえお"); done(); }); });
これを、iconv-liteで。
test("read utf8, using iconv-lite", done => { fs.readFile("data/utf8.txt", (err, data) => { const stringData = iconv.decode(data, "utf-8"); expect(stringData).toEqual("あいうえお"); done(); }); });
iconv#decodeを使って、Bufferを変換します、と。
const stringData = iconv.decode(data, "utf-8");
test("read shift-jis, using iconv-lite", done => { fs.readFile("data/sjis.txt", (err, data) => { const stringData = iconv.decode(data, "windows-31j"); expect(stringData).toEqual("あいうえお"); done(); }); }); test("read euc-jp, using iconv-lite", done => { fs.readFile("data/eucjp.txt", (err, data) => { const stringData = iconv.decode(data, "eucjp"); expect(stringData).toEqual("あいうえお"); done(); }); });
このあたりを見ると、「Windows-31J」や「CP932」は「Shift_JIS」のエイリアスっぽいです。
https://github.com/ashtuchkin/iconv-lite/blob/v0.4.19/encodings/dbcs-data.js#L41-L56
EUC-JPは、ひとつだけだったり。
https://github.com/ashtuchkin/iconv-lite/blob/v0.4.19/encodings/dbcs-data.js#L58-L62
encode
今度は、encode。単純に、Stringに対してencodeしてみます。
まずは、標準APIの範囲で。
test("encode standard utf-8", () => { const data = "あいうえお"; const buffer = Buffer.from(data, "utf-8"); expect(buffer).toEqual(Buffer.from([0xe3, 0x81, 0x82, 0xe3, 0x81, 0x84, 0xe3, 0x81, 0x86, 0xe3, 0x81, 0x88, 0xe3, 0x81, 0x8a])); });
iconv-liteを使って、UTF-8、Shift_JIS、EUC-JPでencode。
test("encode utf-8, using iconv-lite", () => { const data = "あいうえお"; const buffer = iconv.encode(data, "utf-8"); expect(buffer).toEqual(Buffer.from([0xe3, 0x81, 0x82, 0xe3, 0x81, 0x84, 0xe3, 0x81, 0x86, 0xe3, 0x81, 0x88, 0xe3, 0x81, 0x8a])); }); test("encode shift-jis, using iconv-lite", () => { const data = "あいうえお"; const buffer = iconv.encode(data, "windows-31j"); expect(buffer).toEqual(Buffer.from([0x82, 0xa0, 0x82, 0xa2, 0x82, 0xa4, 0x82, 0xa6, 0x82, 0xa8])); }); test("encode euc-jp, using iconv-lite", () => { const data = "あいうえお"; const buffer = iconv.encode(data, "eucjp"); expect(buffer).toEqual(Buffer.from([0xa4, 0xa2, 0xa4, 0xa4, 0xa4, 0xa6, 0xa4, 0xa8, 0xa4, 0xaa])); });
こちらの場合は、iconv#encodeを使用してStringをBufferに変換します、と。
const buffer = iconv.encode(data, "utf-8");
Streaming
ここまでは、iconv#encode/decodeとStringとBufferの変換をやっていましたが、Streming APIというものもあるようです。
ここにある、stream.Readableと合わせて使うみたいですね。
Stream | Node.js v9.11.2 Documentation
fs#createReadStreamに対して、pipeでiconv#decodeStreamを指定。
test("read utf-8 stream, using iconv-lite", done => { fs .createReadStream("data/utf8.txt") .pipe(iconv.decodeStream("utf-8")) .collect((err, data) => { expect(data).toEqual("あいうえお"); done(); }); });
collectってstream.Readableにも載っていなくて、なんだろう?と思ったのですが、iconv-liteで作っているもののようです。
https://github.com/ashtuchkin/iconv-lite/blob/v0.4.19/lib/streams.js#L65-L73
https://github.com/ashtuchkin/iconv-lite/blob/v0.4.19/lib/streams.js#L112-L120
stream.Readableのイベントを使うなら、こんな感じでしょうか。
test("read utf-8 stream and event, using iconv-lite", done => { const stream = fs .createReadStream("data/utf8.txt") .pipe(iconv.decodeStream("utf-8")); stream.on("data", chunk => expect(chunk).toEqual("あいうえお")); stream.on("end", () => done()); });
stream.Readableから、stream.Writableにエンコーディングを変換して流し込み。
test("write shift-jis stream, using iconv-lite", done => { const stream = fs .createReadStream("data/eucjp.txt") .pipe(iconv.decodeStream("eucjp")) .pipe(iconv.encodeStream("windows-31j")) .pipe(fs.createWriteStream("data/sjis-iconv-lite.txt")); stream.on("finish", () => { fs.readFile("data/sjis-iconv-lite.txt", (err, data) => { const stringData = iconv.decode(data, "windows-31j"); expect(stringData).toEqual("あいうえお"); done(); }); }); });
とりあえず、基本的な使い方とBuffer、StreamのAPIにちょこっと触れたところで、今回はおしまいです。