Chapter 06

Unicode の文字幅問題と WindowsTerminal

zetamatta
zetamatta
2021.02.07に更新

文字幅!

Shift-JIS 、というか Double Byte Character System だけの時は実に簡単でした。2バイトの文字は、幅も画面で2マス消費するというシンプルなルールでした。

ところが Unicode 時代になってからそうもいかなくなりました。2バイト以上の文字でも、平気で画面の1マスしか使わないというケースも現れてきたのです。

一応、Unicode 的には、そのあたりの規定もあります。

これによると、

https://www.unicode.org/reports/tr11/
  • East Asian Fullwidth (F)
  • East Asian Halfwidth (H)
  • East Asian Wide (W)
  • East Asian Narrow (Na)
  • East Asian Ambiguous (A)

などとカテゴリ分けされているようです。さて、どれがいわゆる半角(コンソールで1セル消費)・全角(2セル消費)に相当するのでしょうか!?

> 環境やケースによって違います <

おい

(1) Windows 8.1 までのコマンドプロンプト

必ずしも Unicode の定義とは関係ないようです。画面で表示されるフォント幅で半角・全角を判断しているようです。とはいえ、ほとんどのケースで Unicode の定義と一致しています。たまに Ambiguous な文字で差異が発生する程度です。

このあたり、そのための判断に使う Go のライブラリとして、go-windows-consolefontpixelなどというものを作ったりしました。これで目的の文字の横幅ピクセル数がわかるので、それを元に半角・全角判断をできるのではないかと考えていました。が、Windows10 以降では状況が変わっているようなので、途中で放り出してしまいました。

(2) Windows 10 のコマンドプロンプト

10になってから Unicode の定義に準拠するようになったようです。Fullwidth・Wide・Ambiguousは全角扱い、それ以外は半角扱いになっているようです。ただし、Ambiguous はおそらくコードページ・ロケールなどによっては半角になっている可能性もあるのではないかと見ています(詳細は未検証)

(3) WindowsTerminal

コマンドプロンプトで全角であった Ambiguous 文字が半角になっています。
(このあたり、WindowsTerminal 開発陣は「Unicode対応はまだ完全ではない」(The Unicode support is not complete yet)と言っているので、今後変わってくるかもしれません)

ワタクシは拙作のコマンドシェル nyagos 用の一行入力パッケージ go-readline-ny を開発するにあたって、次のような対応を行いました。

  1. (1)は考慮しない(Windows8.1でもそれほど仕様に支障をきたさない)。基本的に Unicode での定義を尊重する。基本は Unicode定義に基づいて文字のセル数を算出してくれる go-runewidth の RuneWidth関数をそのまま使う
  2. WindowsTerminal の場合、つまり環境変数 WT_SESSION と WT_PROFILE_ID 両方ともに1文字以上の文字列が設定されているとき、Ambiguous とされている文字(runewidth.IsAmbiguousWidth(n) )は文字幅1セルとする
  3. さもなければ、RuneWidth関数をそのまま使う

こんな感じで、編集中のテキストがズレるという事態を回避しました。