Go 言語による Unicode 半角/全角変換

もう2年近く前の記事だが,「Go 言語と Unicode 正規化」の脚注でこっそり「単に全角・半角変換ができればいいのなら golang.org/x/text/width パッケージをお勧めする」と書いていたのだが,今回はその話。

golang.org/x/text/width パッケージ

golang.org/x/text/width パッケージ1 では半角/全角変換用に以下の width.Transformer が用意されている。

var (
	// Fold is a transform that maps all runes to their canonical width.
	//
	// Note that the NFKC and NFKD transforms in golang.org/x/text/unicode/norm
	// provide a more generic folding mechanism.
	Fold Transformer = Transformer{foldTransform{}}

	// Widen is a transform that maps runes to their wide variant, if
	// available.
	Widen Transformer = Transformer{wideTransform{}}

	// Narrow is a transform that maps runes to their narrow variant, if
	// available.
	Narrow Transformer = Transformer{narrowTransform{}}
)

width.Widen が全角変換用, width.Narrow が半角変換用である。

たとえば,半角変換なら

fmt.Println(width.Narrow.String("1234567890アイウエオカキクケコABCDEFGHIJK"))
//Outpput
//1234567890アイウエオカキクケコABCDEFGHIJK

全角変換なら

fmt.Println(width.Narrow.String("1234567890アイウエオカキクケコABCDEFGHIJK"))
//Outpput
//1234567890アイウエオカキクケコABCDEFGHIJK

のように変換される。 io.Reader を使うなら

r := bytes.NewBufferString("1234567890アイウエオカキクケコABCDEFGHIJK")
tr := transform.NewReader(r, wdth.Widen)
buf := new(bytes.Buffer)
io.Copy(buf, tr)
fmt.Println(buf)
//Outpput
//1234567890アイウエオカキクケコABCDEFGHIJK

ってな感じで書ける。

面白いのは width.Fold で,半角カナ文字は全角に,全角英数字は半角に,いい感じに変換してくれるのだ。

fmt.Println(width.Fold.String("1234567890アイウエオカキクケコABCDEFGHIJK"))
//Outpput
//1234567890アイウエオカキクケコABCDEFGHIJK

width.Fold は, Unicode 正規化と異なり,「神と神2」のような異体字は

fmt.Println(width.Fold.String("神と神"))
//Outpput
//神と神

と影響を受けない3。 ブラボー!

ちなみに Unicode の円記号(YEN SIGN)は,通常文字(半角相当)が「¥:U+00A54」で全角文字が「¥:U+FFE5」である。 JIS ラテン文字(JIS X 0201)の半角円記号(0x5C)は機械的に変換しても同じ値の「\ (REVERSE SOLIDUS)」のままなので(当たり前だけど)ご注意を5。 そろそろ JIS ラテン文字のことは忘れていいかもねー。

spiegel-im-spiegel/text に組み込んでみた

というわけで,この Unicode 半角/全角変換機能を私作の spiegel-im-spiegel/text v0.5.0 に組み込んでみた。 こんな感じに使える。

res := width.Reader(bytes.NewBufferString("1234567890アイウエオカキクケコABCDEFGHIJK"), width.Fold)
buf := new(bytes.Buffer)
io.Copy(buf, res)
fmt.Println(buf)
// Output:
// 1234567890アイウエオカキクケコABCDEFGHIJK

あと,このパッケージの CLI 実装用に作った「nkf っぽいなにか」にもサブコマンド width として組み込んでいる。

$ gonkf width -h
Convert character width of text (UTF-8 text only)

Usage:
  gonkf width [flags] [text file]

Flags:
  -f, --form string     form of width [fold|narrow|widen] (default "fold")
  -h, --help            help for width
  -o, --output string   output file path

$ echo 1234567890アイウエオカキクケコABCDEFGHIJK | gonkf width -f fold
1234567890アイウエオカキクケコABCDEFGHIJK

たとえば Sjift-JIS テキストに対して機能をパイプで繋いで

$ cat sjis.txt
1234567890アイウエオカキクケコABCDEFGHIJK

$ gonkf conv -s sjis sjis.txt | gonkf nwline -f lf | gonkf width -f fold > utf8.txt

$ cat utf8.txt
1234567890アイウエオカキクケコABCDEFGHIJK

みたいな変換もできる(画面はイメージですw)。

これで概ね spiegel-im-spiegel/text でやりたいことはやり尽くしたかな。

参考図書

photo
プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)
Alan A.A. Donovan Brian W. Kernighan 柴田 芳樹
丸善出版 2016-06-20
評価

スターティングGo言語 (CodeZine BOOKS) Go言語によるWebアプリケーション開発 Kotlinスタートブック -新しいAndroidプログラミング Docker実戦活用ガイド グッド・マス ギークのための数・論理・計算機科学

著者のひとりは(あの「バイブル」とも呼ばれる)通称 “K&R” の K のほうである。

reviewed by Spiegel on 2016-07-13 (powered by G-Tools)


  1. “width” って日本人には発音しにくいよね。「IT業界で横行する恥ずかしい英語発音」でもネタになるほど(笑) [return]
  2. ブラウザやブラウザの設定によっては「神」が上手く表示できないようです。 最初の「神」は「U+FA19 '神'」で CJK 互換文字です。2番目の「神」が通常の「U+795E '神'」です。 [return]
  3. Unicode 正規化と異体字の問題については「Go 言語と Unicode 正規化」を参照のこと。 [return]
  4. 円記号「¥:U+00A5」のコード・ポイントは ISO-8859-1 (Latin-1) で割り当てられている円記号のコードと同じ。というか,そもそも Unicode は ISO-8859-1 と親和するように設計されているのでこうなっているのだが。円記号が ISO-8859-1 に収録されていて(8ビット空間に収まっていて)よかったね,というべきか(笑) [return]
  5. この辺の事情は韓国語の WON SIGN「₩:U+20A9」とかでも同じらしい。 [return]