現象
Qiitaの記事を日々textlintでチェックしていると、たま~に以下のようなエラーが出る。
TypeError: Cannot read property 'length' of undefined
at ViterbiBuilder.build (~\node_modules\kuromoji\src\viterbi\ViterbiBuilder.js:53:48)
at Tokenizer.getLattice (~\node_modules\kuromoji\src\Tokenizer.js:126:33)
at Tokenizer.tokenizeForSentence (~\node_modules\kuromoji\src\Tokenizer.js:81:24)
at ~\node_modules\kuromo
jin\lib\kuromojin.js:60:26
at <anonymous>
catchしてスキップすればいいだけなので困ってはいないが、原因調査。
結論
タイトルどおり、Qiitaの記事に0x00
(NUL)が含まれていたから。
動作検証環境
- Windows7 64bit
- node.js v9.3.0
textlint@10.2.1
kuromoji@0.1.2
text-encoding@0.6.4
原因stringの確認
エラーが出る投稿を確認した。
端末への出力をコピペしたものでは再現しなかったので、
直接ファイルに書き出すことに。
ファイルを読み込んだstringをチェックすることでエラーが再現した
→ stringとtextlint以外の部分がエラーに関与している可能性がなくなった
ファイルを確認すると、通常、あまり目にしない内容が含まれていることが確認できた。
(画像は投稿内容を短く加工後)
一応原因の切り分け
textlintに渡してエラーが出たことを確認したコード
const TextLintEngine = require('textlint').TextLintEngine;
const engine = new TextLintEngine();
const fs = require('fs');
searchTarget = fs.readFileSync('out.txt').toString();
;(async () =>{
const result = await engine.executeOnText(searchTarget).catch(e => {
console.log('textlint error', e);
console.log(searchTarget)
return [{messages : []}];
});
})()
バックトレースがkuromojiなので、textlintの確認はこれぐらいで。
kuromoji単体で0x00
が原因で発生するかを別途installして確認する。
const kuromoji = require("kuromoji");
const path = require("path");
const kuromojiDir = path.dirname(require.resolve("kuromoji"));
const option = {dicPath: path.join(kuromojiDir, "..", "dict")}
const TextDecoder = require('text-encoding').TextDecoder;
const nullbyte = (new TextDecoder).decode(Uint8Array.of(0x00))
kuromoji.builder(option).build(function (err, tokenizer) {
// tokenizer is ready
var path = tokenizer.tokenize(nullbyte);
console.log(path);
});
このコードでも同じエラーが出たので、
- textlint関係なく
- 前後の文字列関係なく
kuromojiに0x00
を渡すとエラーになると決めつけた。
(kuromojiだともうすこしエラーが詳細に)
~\node_modules\kuromoji\src\viterbi\Viterb
iBuilder.js:53
for (var i = 0; i < token_info_ids.length; i++) {
^
TypeError: Cannot read property 'length' of undefined
at ViterbiBuilder.build (~\node_module
s\kuromoji\src\viterbi\ViterbiBuilder.js:53:48)
(後略)
参考
-
azu/kuromojin: Provide a high level wrapper for kuromoji.js
textlintで使っているラッパーだが、kuromojiの呼び出し方のコードを使わしてもらいました。 -
JavaScript、Node.js で文字列とバイト列の相互変換 - Qiita
0x00
文字列の作り方
Node.jsでは別途パッケージのインストールが必要と知らなかったので助かりました。
- takuyaa/kuromoji.js: JavaScript implementation of Japanese morphological analyzer
- inexorabletash/text-encoding: Polyfill for the Encoding Living Standard's API
メモ
ワークアラウンドで回避するなら事前に置換?
null文字はlengthを持っていないのかと思ったが、そうでもないようなので、やはりパーサ側の処理が原因のようだ。
const nullbyte = (new TextDecoder).decode(Uint8Array.of(0x00))
console.log('null', nullbyte.length) // 1
ただ本当に現状はクリティカルな問題ではないので放置している。
(なので原因究明が出来てなくて、Issueも送れていません)