Unicode の East Asian Width 特性値を取得したりするライブラリを作った

また作った報告になってしまい恐縮ですが, 作りましたにゃん.

github.com

East Asian Width (EAW) とは

参考:

元は Unicode の文字を旧来の文字セット (Shift-JIS や EUC-JP など) にマップする際に半角・全角のどちらにすべきかというヒントですが, 文字の幅を取得するための簡便な手段として使われることも (ことが?) 多いです.

例えば macOS のターミナルで「Unicode 東アジア A (曖昧) の文字幅を W (広) にする」という設定を有効にしたことのある方も多いと思いますが, それが EAW に関する設定です.

実装

EAW 特性値は以下のファイルに定義されています.

http://www.unicode.org/Public/UCD/latest/ucd/EastAsianWidth.txt

これは 2000 行程度あって威圧感がありますが, 隣接している区間が同じ特性値であっても別の行に書かれていたりするので, 判定に必要な情報としてはもう少し圧縮できます.

また, このファイルに明示されていないものもあり, それらは特性値 N とするとあるので, 補完しつつ処理してやると約 600 エントリ弱になります.

あとは二分探索とかで符号位置に対応する特性値を見つけてやれば OK です.

文字の幅の計算については各特性値をどのように扱うべきかが書かれているので, これに従って計算してやります. W, F は常に全角, Na, H, (N) は常に半角として良くて, A のみ文脈によって変えられるようにするくらいです.

他のライブラリとの比較

この種のライブラリはもちろん今回私が作ったものが最初ではなく, 既にいくつか存在しました. npm のダウンロード数を見る限り, eastasianwidth がよく使われているようです (私も以前はこれを使っていました).

ではなぜわざわざ新しく作ったかというと, markdown-table-editor では, 上で例に挙げた macOS のターミナルと同様, EAW 特性値が A の文字幅を設定で変えられるようにしているのですが, これを実装するために適したものが無かったからです.

まず eastasianwidth は参照している Unicode のファイルのバージョンが古いため, 例えば 🍣 などの幅が正しく判定できなかったりします (最新版は 9.0 なのに対し, eastasianwidth が参照しているのは 6.0?). 更新しようにもソースの自動生成スクリプトがなく, とても手で直す気にもなれませんでした. 他にも A の文字幅が全角扱いで変更できあかったり, length()サロゲートペアを考慮しないバグがあったり, slice() の実装の効率が悪そうだったりするので, あまりおすすめできません.

また eaw については新しいものの文字幅の測定に特化しており特性値の取得はできず, さらに A, N の文字が全角になっていて変更できないため, 残念ながら今回の目的には適していませんでした.

というか既存のライブラリを見ると特性値 A の文字を常に全角で扱いがちです. “If the context cannot be established reliably, they should be treated as narrow characters by default.” って言ってるのだから, 文脈の判断をちゃんと実装しないならデフォルトで半角扱いにしてください. なんで強制全角扱いなんですか, ギリシャ文字を非 EA 文脈で使うこととか無いんですか? (半ギレ)

meaw では上で説明したように特性値を取得でき, さらに文字幅の計算時には特性値によって幅を自由に変えられるようにしてあります. これで安心ですね. 速度についても二分探索のお陰か eastasianwidth よりは高速です.

まとめ

というわけで EAW 特性値を知りたくなったり, 柔軟な文字幅の測定が行いたくなったときは是非 meaw をお使いください.