LoginSignup
4

More than 3 years have passed since last update.

ゴゴコ゛ゴゴゴコ゛をゴゴゴゴゴゴゴにするGo言語のライブラリを作ってみた

Posted at

はじめに

Unicode 正規化って日本語の処理に向いていないというか、ちょっと使いどころが難しいと思いませんか?

ということで、Golang のノーマライゼーション・ライブラリを作ってみました。使えるレベルに仕上がったと思うので GaGa という名前で公開します。

Unicode 正規化に関しては、既に Golang の準標準ライブラリに、

  • golang.org/x/text/unicode/norm
  • golang.org/x/text/width

というパッケージがありますが、今回作ったものと機能を比較すると以下のような違いがあります。

機能など gaga norm width 補足
全角-半角
変換
詳細は後述しますが、norm と width は期待外れな結果を返します。
ひらがな-
カタカナ
変換
× × 「ひらがな-カタカナ変換器」はノーマライザというよりはトランスレータの類かもしれませんね。
大文字-
小文字
変換
× × これは Golang の標準ライブラリ strings パッケージでサポートされていますね。これもトランスレータの類でしょうか。
濁点・半濁点
の分解合成
これも norm と width は期待外れな振舞いをします。詳細は後述します。
文字種別ごとの変換 × × gaga は アルファベット、ラテン数字、ラテン記号、ひらがな、カタカナ、かな記号ごとに変換方法を指定できます。
パフォーマンス 処理時間を比較するとこんな感じです。
gaga : norm : width ≒ 2 : 6 : 1
サポートする文字 gaga のサポート範囲はラテン文字とひらがなカタカナを中心とする462文字です。

ちなみに、Golang の準標準ライブラリの振舞いが期待外れなのは、開発チームや実装に問題があるということではありません。世界中のありとあらゆる文字を取り扱わなければならない Unicode の標準化において、多種多様な文化間のバランスを取りながら最大公約数的に策定せざるを得なかった正規化フォームの仕様が、「かな文字」をネイティブとして扱う我々にとっては完全なものではないということなのだと思います。

目次

Unicode の 7 種類の「ゴ」について

Unicode でカタカナの「ゴ」と書くとき、多くの人はコードポイント U+30B4(名称: KATAKANA LETTER GO)を使うと思います。

ところが歴史的な事情により UNICODE には 3 種類の「濁点マーク」が登録されていて、

3 種類の濁点マーク

グリフ Unicode JIS 名前
U+309B 212B
(JIS X 0208)
KATAKANA-HIRAGANA VOICED SOUND MARK
U+FF9E DE
(JIS X 0201)
HALFWIDTH KATAKANA VOICED SOUND MARK
◌゙ U+3099 (undefined) COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK

これに濁点なしの全角の「コ」と半角の「コ」を組み合わせると、「ゴ」という 1 つの文字を 7 通りの方法で表現できてしまいます。

7 通りの「ゴ」

No. グリフ 構成 コードポイント 説明
1 [ ] [ U+30B4 ] [ 濁点付き 全角文字 ]
2 コ゛ [ ] +
[ ]
[ U+30B3 ] +
[ U+309B ]
[ 濁点なし 全角文字 ] +
[ 全角 濁点 ]
3 ゴ [ ] +
[ ]
[ U+30B3 ] +
[ U+FF9E ]
[ 濁点なし 全角文字 ] +
[ 半角 濁点 ]
4 ゴ [ ] +
[ ]
[ U+30B3 ] +
[ U+3099 ]
[ 濁点なし 全角文字 ] +
[ 幅なし 濁点 ]
5 コ゛ [ ] +
[ ]
[ U+FF7A ] +
[ U+309B ]
[ 濁点なし 半角文字 ] +
[ 全角 濁点 ]
6 ゴ [ ] +
[ ]
[ U+FF7A ] +
[ U+FF9E ]
[ 濁点なし 半角文字 ] +
[ 半角 濁点 ]
7 ゴ [ ] +
[ ]
[ U+FF7A ] +
[ U+3099 ]
[ 濁点なし 半角文字 ] +
[ 幅なし 濁点 ]

上表のグリフ欄の見え方は環境によって変わりますが、筆者の環境(Windows 10 + Chrome 85)では、上記 1 と 4、また 5 〜 7 は見た目上の違いがわかりません。

また、これは特にラテン文字に顕著ですが、プロポーショナルフォントが当たり前になった現代では、全角と半角の違いが昔より分かりにくくなりました。

そして、この「似ているのに異なる」ことが、

  • 検索してても見つからない
  • 統計を取る際のカテゴライズに苦労する

などといった、地味だけど面倒な問題を引き起こします。

この問題の対処方法はいくつかありますが、

No. アプローチ ソリューションの具体例 補足
1 入力制限 Validation 検査 登録フォームなど情報の入口で入力できる文字を制限し、正規形のみを通過させるとてもポピュラーなアプローチ。
2 選択 プルダウンメニューなど 入力制限の一種。選択肢が少ないときは便利。
3 同値とみなす DBMS の Collation 情報の出口での対処方法。データベースの文字セットに照合順序の属性を持たせ、比較の際に例えば半角と全角、大文字と小文字を同値とみなすアプローチ。
4 形を揃える Unicode Normalization 正規化して形を整えてから比較するアプローチ。情報の入口でも適用できるが、Webサイトの登録フォームなどではあまり見かけない。

本稿のテーマは上記 4 に関するものです。

誰かがついうっかり「ゴー言語」や「コ゛ー言語」と入力してしまっても、由緒正しい「ゴー言語」ではないからといって、「その文字は入力できません!」などと拒絶するのではなく、そっと優しく正規の形に導いてあげませんか? そのためのライブラリを作りました!という話です。

Combining characters について

上述のとおり、Unicode には、JIS 由来の濁点マーク [゛](U+309B)、[゙](U+FF9E) の他に、端末でレンダリングする際に基底文字に合成してレイアウトされる「幅ゼロの濁点マーク」[◌゙](U+3099) が登録されていて、その名称には "COMBINING" というプレフィクスが付けられています。
(半濁点にも同様に [ ゚ ](U+309A) があります)

普段の生活でこの Combining character (Non-space mark) を使う機会は少ないと思いますが、Unicode 正規化フォームの「正準等価性による分解と合成」においてもキーになる文字なので少しだけ触れておきます。

Combining character のメリット

かつて JIS X 0208 には という文字はありましたが、 はありませんでした。
JIS X 0213 では が登録され、他にも、(濁点付きの [ワ])、カ゚(半濁点付きの [カ])なども登録されました。

ところが、JIS X 0213 に はあるのに、わ゙ はありません。
また、Unicode への カ゚ の登録は見送られました。見送られた理由は Combining character の U+309A で合成できるから、ということのようです。

整理すると以下のようになります。

特殊な濁点付き文字の例

グリフ
(Unicode)
代用表記
(JIS X 0208)
JIS X 0213
のサポート
Unicode
のサポート
補足
う゛
ワ゛
わ゙ わ゛ × × グリフ欄:
[わ]+[U+3099]で表示
カ゚ カ゜ × グリフ欄:
[カ]+[U+309A]で表示

また、以下のような文字も JIS に登録されていません。
(JIS に登録されていないので、当然 Unicode にも登録されていません)

用途 グリフ
Unicode
代用表記
JIS X 0208
補足
ジャイアントロボの決め台詞 マ゙ マ゛ グリフ欄:
[マ]+[U+3099]で表示
一部の官能表現など ア゙ ア゛ グリフ欄:
[ア]+[U+3099]で表示

近年、アーティスト名の表現方法が多彩になってきていますが、今後、濁音を持たない文字に濁点や半濁点を組み合わせた名前も出てくるかもしれません。

Combining character を使えば、新たにコードポイントを追加しなくても、このような要求を満たすことができます。

Combining character のデメリット

現時点ではまだ普及しているとはいえないことが挙げられるでしょう。
環境によって見え方が変わってしまうのです。

同じプラットフォームで同じアプリケーションを使っていても、フォントにより見え方がまったく違います。
以下は筆者の環境で、ひとつの Excel シート内に、Combining Character を含む同一の文字列をフォントだけ変えて表示した例です。

image.png

編集時の挙動もアプリケーションによって違います。
アプリケーションによって、濁点だけを削除することができたり、先行の文字にくっついて削除できなかったりと、違いがあります。
興味がある人は以下の文字列をエディタなどにコピペして試してみてください。

え゚?゚ G゙A゙G゙A゙が゙?゙

このように、Combining character はまだまだ普及しているとは言えません。現段階ではキャラクターベースのコミュニケーション用途には向きません。どうしても使いたいという場合は、環境要件を固定するか、特定の環境でラスタライズしたものを共有するなどの対応が必要になるでしょう。

Unicode 正規化フォームの問題点

問題点について述べる前に Unicode 正規化フォーム (Unicode Normalization Form) について簡単に整理しておきます。

まず用語ですが、本稿では以下の用語を使って話を進めます。

用語 別名
(かな文字の場合)
略称
Voicing modifier 発声修飾子
(濁点または半濁点)
[] [] VOM
Pre-composed character 合成済み文字
(濁点付き文字)
[] Precomp
Base character 基底文字
(濁点なし文字)
[] Base
Combining character 結合文字
(幅なしのVOM)
※全角と半角のVOMは含まない
[ ◌゙ ] Combining

次に Unicode の「正準等価性」と「互換等価性」について整理します。

正準等価性

まずは、正準等価性です。
正準等価性というのは、かな文字で言えば濁点付き文字(または半濁点付き文字)の等価性のことで、分解と合成により別の等価な文字シーケンスに置き換えることができます。

正準等価性による分解・合成

正規化の種類 かな文字の挙動
分解 Precomp を Base と Combining に分解する。 [] => [] + [ ◌゙ ]
合成 Base と Combining を Precomp に合成する。 [] + [ ◌゙ ] => []

本稿のテーマからは逸れますが、ラテン文字のダイアクリティカルマークなども、正準等価性による分解と合成をすることで、これとよく似た変換が行われます。

互換等価性

次に、互換等価性です。
互換等価性は、かな文字で言えば全角と半角の等価性のことです。
分解により、かな文字は全角へ、ラテン文字は半角へ変換されます。

また、かな文字以外では「互換等価性による分解」で「文字幅」が変化するとは限らないので注意が必要です。
例えば、[㍉] という全角の非漢字文字は、 [ミ][リ] という全角カタカナ 2 文字に分解されます。

互換等価性による分解

正規化の種類 かな文字の挙動
分解 半角文字を全角文字にする。
(ラテン文字の場合は逆)
[] => []
[
] => [ A ]

なお、「互換等価性による合成」という言葉は Uicode 正規化の仕様には出てこないようですが、かな文字の全角から半角への変換はこれにあたると考えることもできるでしょう。

Golang の準標準ライブラリ

冒頭で触れたとおり、Golang の準標準ライブラリには Unicode Normalization Form 関連のパッケージが 2 つありますが、どちらもかな文字の扱いに関して問題があり、期待した結果を返してくれません。

golang.org/x/text/width の問題点

ひとつは golang.org/x/text/width パッケージで、これは互換等価性の分解と合成に近い働きをするものです。
(先に述べた通り互換等価性による合成という言葉は Unicode の仕様には出てきません)

要するに全角 (Full/Wide) と半角 (Half/Narrow) の変換をしてくれるものなのですが、このパッケージで 7 種類の「ゴ」を変換してみると可愛げのない挙動になります。

確認用のプログラムは以下のとおりです。

width_examples.go
package main

import (
        "fmt"
        "golang.org/x/text/width"
        "strings"
)

func hexs(s string) string {
    var ss []string
    for _, r := range []rune(s) {
        e := fmt.Sprintf("%04X", r)
        ss = append(ss, e)
    }
    switch len(ss) {
    case 0:
        return "<empty>"
    case 1:
        return "[" + ss[0] + "]"
    default:
        return "[" + strings.Join(ss, " ") + "]"
    }
}

func main() {
        s := "ゴコ゛コ\u3099ゴゴコ゛コ\u3099"
        narrow := width.Narrow.String(s)
        widen := width.Widen.String(s)
        fold := width.Fold.String(s)

        fmt.Printf("SRC :\t%q,\n\t%s\n", s, hexs(s))
        fmt.Printf("NAR :\t%q,\n\t%s\n", narrow, hexs(narrow))
        fmt.Printf("WIDE:\t%q,\n\t%s\n", widen, hexs(widen))
        fmt.Printf("FOLD:\t%q,\n\t%s\n", fold, hexs(fold))
}

実行結果は以下のとおりです。

結果
$ go run width_examples.go 
SRC :   "ゴコ゛ゴゴゴコ゛ゴ",
        [FF7A FF9E FF7A 309B FF7A 3099 30B4 30B3 FF9E 30B3 309B 30B3 3099]
NAR :   "ゴコ゛ゴゴゴコ゛ゴ",
        [FF7A FF9E FF7A 309B FF7A FF9E 30B4 FF7A FF9E FF7A 309B FF7A FF9E]
WIDE:   "ゴコ゛ゴゴゴコ゛ゴ",
        [30B3 3099 30B3 309B 30B3 3099 30B4 30B3 3099 30B3 309B 30B3 3099]
FOLD:   "ゴコ゛ゴゴゴコ゛ゴ",
        [30B3 3099 30B3 309B 30B3 3099 30B4 30B3 3099 30B3 309B 30B3 3099]

結果を整理すると以下のようになります。
(具合の悪い箇所を赤くしています)

Source ゴ
FF7A
FF9E
コ゛
FF7A
309B
ゴ
FF7A
3099

30B4
 
ゴ
30B3
FF9E
コ゛
30B3
309B

30B3
3099
問題点
Narrow ゴ
FF7A
FF9E

FF7A
309B
ゴ
FF7A
FF9E

30B4
 
ゴ
FF7A
FF9E

FF7A
309B
ゴ
FF7A
FF9E
Wide VOM(309B) が Narrow にならない。
Wide Precomp(30B4) が Narrow にならない。
Widen
30B3
3099
コ゛
30B3
309B

30B3
3099

30B4
 

30B3
3099
コ゛
30B3
309B

30B3
3099
3 つとも Precomp(30B4) になってほしい。
せめて Base + Combining (30B3+3099) に揃えてほしい。
Fold
30B3
3099
コ゛
30B3
309B

30B3
3099

30B4
 

30B3
3099
コ゛
30B3
309B

30B3
3099
Fold は Latin を Narrow に、Kana を Wide にする Transformer。問題点は Widen と同様。

golang.org/x/text/unicode/norm の問題点

もうひとつは golang.org/x/text/unicode/norm で、4 種類の Unicode Normalization Form をサポートしています。

4 種類の Unicode 正規化形式

形式 説明 かな文字の場合
NFD 正準等価性によって分解される。 濁点や半濁点が基底文字から分離する。Precomp => Base + Combining
NFC 正準等価性によって分解された後に、正準等価性によって合成される。 濁点や半濁点が基底文字から分離した後に再度合成される。
Precomp => Base + Combining,
Base + Combining => Precomp
NFKD 互換等価性によって分解される。 半角カタカナが全角カタカナになる。
Narrow => Wide
NFKC 互換等価性によって分解された後に、正準等価性によって合成される。 半角カタカナが全角カタカナに変換された後に、基底文字と合成文字が合成される。
Narrow => Wide,
Base + Combining => Precomp

7 種類の「ゴ」を変換してみます。

norm_examples.go
package main

import (
        "fmt"
        "golang.org/x/text/unicode/norm"
        "strings"
)

func hexs(s string) string {
    var ss []string
    for _, r := range []rune(s) {
        e := fmt.Sprintf("%04X", r)
        ss = append(ss, e)
    }
    switch len(ss) {
    case 0:
        return "<empty>"
    case 1:
        return "[" + ss[0] + "]"
    default:
        return "[" + strings.Join(ss, " ") + "]"
    }
}

func main() {
        s := "ゴコ゛コ\u3099ゴゴコ゛コ\u3099"
        nfd := norm.NFD.String(s)  // Canonical decomposition
        nfkd := norm.NFKD.String(s) // Compatibility decomposition
        nfc := norm.NFC.String(s)  // Canonical decomposition -> Canonical composition
        nfkc := norm.NFKC.String(s) // Compatibility decomposition -> Ccanonical composition

        fmt.Printf("SRC :\t%q,\n\t%s\n", s, hexs(s))
        fmt.Printf("NFD :\t%q,\n\t%s\n", nfd, hexs(nfd))
        fmt.Printf("NFKD:\t%q,\n\t%s\n", nfkd, hexs(nfkd))
        fmt.Printf("NFC :\t%q,\n\t%s\n", nfc, hexs(nfc))
        fmt.Printf("NFKC:\t%q,\n\t%s\n", nfkc, hexs(nfkc))

}

実行結果は以下のとおりです。

結果
$ go run norm_examples.go 
SRC :   "ゴコ゛ゴゴゴコ゛ゴ",
        [FF7A FF9E FF7A 309B FF7A 3099 30B4 30B3 FF9E 30B3 309B 30B3 3099]
NFD :   "ゴコ゛ゴゴゴコ゛ゴ",
        [FF7A FF9E FF7A 309B FF7A 3099 30B3 3099 30B3 FF9E 30B3 309B 30B3 3099]
NFKD:   "ゴコ ゙ゴゴゴコ ゙ゴ",
        [30B3 3099 30B3 0020 3099 30B3 3099 30B3 3099 30B3 3099 30B3 0020 3099 30B3 3099]
NFC :   "ゴコ゛ゴゴゴコ゛ゴ",
        [FF7A FF9E FF7A 309B FF7A 3099 30B4 30B3 FF9E 30B3 309B 30B4]
NFKC:   "ゴコ ゙ゴゴゴコ ゙ゴ",
        [30B4 30B3 0020 3099 30B4 30B4 30B4 30B3 0020 3099 30B4]

結果を整理すると以下のようになります。

Source ゴ
FF7A
FF9E
コ゛
FF7A
309B
ゴ
FF7A
3099

30B4
 
ゴ
30B3
FF9E
コ゛
30B3
309B

30B3
3099
問題点
NFD
FF7A
FF9E

FF7A
309B
ゴ
FF7A
3099

30B3
3099

30B3
FF9E

30B3
309B

30B3
3099
Combining ではない VOM が無視される。
NFC
FF7A
FF9E

FF7A
309B
ゴ
FF7A
3099

30B4
 

30B3
FF9E

30B3
309B

30B4
 
NFD と同様。
NFKD
30B3
3099

30B3
0020
3099

30B3
3099

30B3
3099

30B3
3099

30B3
0020
3099

30B3
3099
全角の VOM が Whitespace + Combining になる。
NFKC
30B4
 

30B3
0020
3099

30B4
 

30B4
 

30B4
 

30B3
0020
3099

30B4
 
全角の VOM が Wthitespace + Combining になるため、再合成されない。

仕様通りに振舞っているのだとは思います。
でも、このパッケージをありがたいと感じている日本人は少ないのではないでしょうか。

どんなライブラリを作ったのか?

日本語文字列ユーティリティです。
ノーマライザは、日本語のロケールで良く使われる 426 文字(ASCII 互換のラテン文字と JIS 互換のひらがなカタカナなど)をサポートしており、文字列の縦書き関数などを同梱しています。

ダウンロード

ソースコードは GitHub に置いてあります。
image.png
go-gaga (Japanese language utility)
README

Golang の環境があれば、以下のコマンドでライブラリが使えるようになります。

console
$ go get github.com/y-bash/go-gaga

使い方

まず、正規化ルールをフラグで指定して Normalizer を生成します。
(エラー処理については後述します)

n, err := gaga.Norm(gaga.LatinToNarrow | gaga.KanaToWide)

あとは String() メソッドを呼ぶだけです。

s := n.String("変換したい文字列")

途中でフラグを変更することもできます。
(エラー処理については後述します)

err = n.SetFlag(gaga.LatinToWide)
s = n.String("変換したい文字列")

もう少し具体的なコードの例を示します。
以下のように文字種別ごとに半角全角の変換、濁点・半濁点の結合、分解、カタカナひらがなの変換、大文字小文字の変換が行えます。

gaga_examples.go
package main

import (
    "fmt"
    "github.com/y-bash/go-gaga"
)

func main() {
    s := "ゴコ゛コ\u3099ゴゴコ゛コ\u3099"
    n, _ := gaga.Norm(gaga.ComposeVom)   // 濁点と半濁点を適切に結合する
    fmt.Println(n.String(s))             // => ゴゴゴゴゴゴゴ

    n, _ = gaga.Norm(gaga.KanaToNarrow)  // ひらがなとカタカナを半角にする
    fmt.Println(n.String(s))             // => ゴゴゴゴゴゴゴ

    n, _ = gaga.Norm(gaga.KanaToWide)    // カタカナを全角にする
    fmt.Println(n.String(s))             // => ゴゴゴゴゴゴゴ

    n, _ = gaga.Norm(gaga.KanaToHiragana) // カタカナをひらがなにする
    fmt.Println(n.String(s))              // => ごごごごごごご

    s = "abcアイウ"
    n, _ = gaga.Norm(
        gaga.LatinToNarrow | // ラテン文字を半角に、
        gaga.AlphaToUpper |  // アルファベットを大文字に、
        gaga.KanaToHiragana) // カタカナをひらがなにする
    fmt.Println(n.String(s)) // => ABCあいう
}

gaga で width や norm と同様のプログラムを書いてみます。

gaga_examples.go
package main

import (
        "fmt"
        "github.com/y-bash/go-gaga"
        "strings"
)

func hexs(s string) string {
    var ss []string
    for _, r := range []rune(s) {
        e := fmt.Sprintf("%04X", r)
        ss = append(ss, e)
    }
    switch len(ss) {
    case 0:
        return "<empty>"
    case 1:
        return "[" + ss[0] + "]"
    default:
        return "[" + strings.Join(ss, " ") + "]"
    }
}

func main() {
        s := "ゴコ゛コ\u3099ゴゴコ゛コ\u3099"
        n, _ := gaga.Norm(gaga.ComposeVom)
        co := n.String(s)

        n.SetFlag(gaga.DecomposeVom)
        de:= n.String(s)

        n.SetFlag(gaga.KanaToNarrow)
        na:= n.String(s)

        n.SetFlag(gaga.KanaToWide)
        wi:= n.String(s)

        n.SetFlag(gaga.KanaToHiragana)
        hi := n.String(s)

        n.SetFlag(gaga.KatakanaToHiragana | gaga.DecomposeVom)
        hd := n.String(s)

        fmt.Printf("SRC            :%q,\n\t%s\n", s, hexs(s))
        fmt.Printf("ComposeVom     :%q,\n\t%s\n", co, hexs(co))
        fmt.Printf("DecomposeVom   :%q,\n\t%s\n", de, hexs(de))
        fmt.Printf("KanaToNarrow   :%q,\n\t%s\n", na, hexs(na))
        fmt.Printf("KanaToWide     :%q,\n\t%s\n", wi, hexs(wi))
        fmt.Printf("KanaToHiragana :%q,\n\t%s\n", hi, hexs(hi))
        fmt.Printf("Hiragana&DecVom:%q,\n\t%s\n", hd, hexs(hd))
}

結果は以下のとおりです。

結果
$ go run gaga_examples.go 
SRC            :"ゴコ゛ゴゴゴコ゛ゴ",
        [FF7A FF9E FF7A 309B FF7A 3099 30B4 30B3 FF9E 30B3 309B 30B3 3099]
ComposeVom     :"ゴゴゴゴゴゴゴ",
        [FF7A FF9E FF7A FF9E FF7A FF9E 30B4 30B4 30B4 30B4]
DecomposeVom   :"ゴゴゴゴゴゴゴ",
        [FF7A 3099 FF7A 3099 FF7A 3099 30B3 3099 30B3 3099 30B3 3099 30B3 3099]
KanaToNarrow   :"ゴゴゴゴゴゴゴ",
        [FF7A FF9E FF7A FF9E FF7A FF9E FF7A FF9E FF7A FF9E FF7A FF9E FF7A FF9E]
KanaToWide     :"ゴゴゴゴゴゴゴ",
        [30B4 30B4 30B4 30B4 30B4 30B4 30B4]
KanaToHiragana :"ごごごごごごご",
        [3054 3054 3054 3054 3054 3054 3054]
Hiragana&DecVom:"ごごごごごごご",
        [3053 3099 3053 3099 3053 3099 3053 3099 3053 3099 3053 3099 3053 3099]

結果を整理すると以下のようになります。

Source ゴ
FF7A
FF9E
コ゛
FF7A
309B
ゴ
FF7A
3099

30B4
 
ゴ
30B3
FF9E
コ゛
30B3
309B

30B3
3099
ComposeVom ゴ
FF7A
FF9E
ゴ
FF7A
FF9E
ゴ
FF7A
FF9E

30B4
 

30B4
 

30B4
 

30B4
 
DecomposeVom ゴ
FF7A
3099
ゴ
FF7A
3099
ゴ
FF7A
3099

30B3
3099

30B3
3099

30B3
3099

30B3
3099
KanaToNarrow ゴ
FF7A
FF9E
ゴ
FF7A
FF9E
ゴ
FF7A
FF9E
ゴ
FF7A
FF9E
ゴ
FF7A
FF9E
ゴ
FF7A
FF9E
ゴ
FF7A
FF9E
KanaToWide
30B4
 

30B4
 

30B4
 

30B4
 

30B4
 

30B4
 

30B4
 
KanaToHiragana
3054
 

3054
 

3054
 

3054
 

3054
 

3054
 

3054
 
KatakanaToHiragana |
DecomposeVom

3053
3099

3053
3099

3053
3099

3053
3099

3053
3099

3053
3099

3053
3099

エラーハンドリングについて

上記のコードでは見やすさのために省略していましたが、実際に使う際はエラーを適切にハンドリングする必要があります。gaga.Norm() も、gaga.Normalizer.SetFlag() も、存在しない値や排他的なフラグの組み合わせ(例えば KanaToNarrow | KanaToWide など)が指定されると error を返します。

以下にエラーハンドリングの例を記しておきます。

gaga_error.go
package main

import (
        "fmt"
        "github.com/y-bash/go-gaga"
        "log"
)

func main() {
        n, err := gaga.Norm(gaga.LatinToWide | gaga.AlphaToUpper)
        if err != nil {
          log.Fatal(err)
        }
        fmt.Println(n.String("aaa"))
        fmt.Println(n.String("bbb"))
        fmt.Println(n.String("ccc"))

        err = n.SetFlag(gaga.LatinToNarrow | gaga.SymbolToWide)
        if err != nil {
          log.Fatal(err)
        }
        fmt.Println(n.String("aaa"))
        fmt.Println(n.String("bbb"))
        fmt.Println(n.String("ccc"))
}
結果
$ go run gaga_error.go 
AAA
BBB
CCC
2020/09/20 08:57:21 invalid normalization flag: (AlphaToNarrow | DigitToNarrow | SymbolToNarrow | SymbolToWide), invalid combination: (SymbolToNarrow | SymbolToWide)
exit status 1

コマンドラインツール

go build や go install でライブラリと同じ機能を持つコマンドラインツールをビルドできます。
Linux と Windows で動作確認しています。
norm コマンド README

フラグの種類

変換ルールを指示するための 20 個のフラグ定数(単独フラグ)が用意されています。
ノーマライザを生成する際にこれを指定して使うのですが、その際、フラグをビット OR 演算子で組み合わせて指定することができます。

また、複数のフラグを組み合わせた定数(コンビネーションフラグ)が 8 個用意されているので、通常はこちらを使う方が便利でしょう。

コンビネーションフラグ (8個)

フラグ 意味 同等の組み合わせ
LatinToNarrow アルファベット、ラテン数字、ラテンシンボルを半角にする。
【例】
"A1?" => "A1?"
AlphaToNarrow | DigitToNarrow | SymbolToNarrow
LatinToWide アルファベット、ラテン数字、ラテンシンボルを全角にする。
【例】
"A1?" => "A1?"
AlphaToWide | DigitToWide | SymbolToWide
KanaToNarrow ひらがな、カタカナ、仮名シンボルを半角にする。濁点および半濁点を従来の方法で結合する。独立した濁点半濁点を半角にする。
【例】
"あイ、が゛" => "アイ、ガ゙"
HiraganaToNarrow | KatakanaToNarrow | KanaSymbolToNarrow | IsolatedVomToNarrow | ComposeVom
KanaToWide カタカナ、仮名シンボルを全角にする。濁点および半濁点を従来の方法で結合する。独立した濁点半濁点を全角にする。
【例】
"ア、ガ゙" => "ア、ガ゛"
KatakanaToWide | KanaSymbolToWide | IsolatedVomToWide | ComposeVom
KanaToWideKatakana ひらがなを全角カタカナにする。半角カタカナを全角カタカナにする。仮名シンボルを全角にする。濁点および半濁点を従来の方法で結合する。独立した濁点半濁点を全角にする。
【例】
"あイ、ガ゙" => "アイ、ガ゛"
KatakanaToWide | HiraganaToKatakana | KanaSymbolToWide | IsolatedVomToWide | ComposeVom
KanaToNarrowKatakana ひらがなを半角カタカナにする。全角カタカナを半角カタカナにする。仮名シンボルを半角にする。濁点および半濁点を従来の方法で結合する。独立した濁点半濁点を半角にする。
【例】
"あイ、が゛" => "アイ、ガ゙"
KatakanaToNarrow | HiraganaToNarrow | KanaSymbolToNarrow | IsolatedVomToNarrow | ComposeVom
KanaToHiragana 全角カタカナと半角カタカナをひらがなにする。仮名シンボルを全角にする。濁点および半濁点を従来の方法で結合する。独立した濁点半濁点を全角にする。
【例】
"アイ、ガ゛" => "あい、が゛"
KatakanaToHiragana | KanaSymbolToWide | IsolatedVomToWide | ComposeVom
Fold 全角アルファベット、数字、シンボルを半角にする。半角カタカナを全角カタカナにする。半角仮名シンボルを全角にする。濁点および半濁点を従来の方法で結合する。独立した濁点半濁点を全角にする。
【例】
"A1?ア、ガ゙" => "A1?ア、ガ゛"
LatinToNarrow | KanaToWide

単独フラグ (20個)

フラグ 意味
AlphaToNarrow 全角アルファベットを半角にする。
【例】
[A]=>[A]
AlphaToWide 半角アルファベットを全角にする。
【例】
[A]=>[A]
AlphaToUpper 半角アルファベットと全角アルファベットを大文字にする。
【例】
[a]=>[A], [a]=>[A]
AlphaToLower 半角アルファベットと全角アルファベットを小文字にする。
【例】
[A] => [a], [A] => [a]
DigitToNarrow 全角ラテン数字を半角にする。
【例】
[1]=>[1]
DigitToWide 半角ラテン数字を全角にする。
【例】
[1]=>[1]
SymbolToNarrow 全角ラテン・シンボルを半角にする。
【例】
[?]=>[?]
SymbolToWide 半角ラテン・シンボルを全角にする。
【例】
[?]=>[?]
HiraganaToNarrow ひらがなを半角カタカナにする。
【例】
[あ]=>[ア]
HiraganaToKatakana ひらがなを全角カタカナにする。
【例】
[あ]=>[ア]
KatakanaToNarrow 全角カタカナを半角にする。
【例】
[ア]=>[ア]
KatakanaToWide 半角カタカナを全角にする。
【例】
[ア]=>[ア]
KatakanaToHiragana 半角カタカナと全角カタカナをひらがなにする。
【例】
[ア] => [あ]
[ア] => [あ]
KanaSymbolToNarrow 全角仮名シンボルを半角にする。
【例】
[、]=>[、]
KanaSymbolToWide 半角仮名シンボルを全角にする。
【例】
[、]=>[、]
ComposeVom 濁点と半濁点を従来の方法で結合する。
【例】
[か][゛]=>[が]
[か][U+3099] => [が]
[か][゙]=>[が]
[カ][゛]=>[カ][゙]
[カ][゙]=>[カ][゙]
[カ][U+3099]=>[カ][゙]
[は][゜]=>[ぱ]
[ヰ][゛]=>[ヸ]

Pre-composed character を持たないひらがなカタカナに続く濁点と半濁点は、先行する文字と同じ幅に変換される。
[あ][゙]=>[あ][゛]
[あ][U+3099]=>[あ][゛]
[ア][゛]=>[ア][゙]
[ア][U+3099]=>[ア][゙]
[ゐ][゛]=>[ゐ][゛]

Pre-composed character に続く濁点と半濁点はこのフラグでは変換されない。
【例】[が][゛]=>[が][゛]
[が][U+3099]=>[が][U+3099]
[が][゙]=>[が][゙]

ひらがなカタカナ以外の文字に続く濁点と半濁点はこのフラグでは変換されない。
【例】
[日][゛]=>[日][゛]
[日][U+3099]=>[日][U+3099]
[日][゙]=>[日][゙]
DecomposeVom 濁点と半濁点を Unicode Normalization Form の正準等価による分解に似た方法で分解する。
【例】
[が]=>[か][U+3099]
[か][゛]=>[か][U+3099]
[か][゙]=>[か][U+3099]
[カ][゛]=>[カ][U+3099]
[カ][゙]=>[カ][U+3099]
[は][゜]=>[は][U+309A]
[あ][゛]=>[あ][U+3099]


Pre-composed character に続く濁点と半濁点はこのフラグでは変換されない。

ひらがなカタカナ以外の文字に続く濁点と半濁点はこのフラグでは変換されない。
IsolatedVomToNarrow Base character に接続されない独立した濁点と半濁点を半角にする。
【例】
[゛]=>[゙]
[U+3099]=>[゙]
[゜]=>[゚]
[U+309A]=>[゚]
IsolatedVomToWide Base character に接続されない独立した濁点と半濁点を全角にする。
【例】
[゙]=>[゛]
[U+3099]=>[゛]
[゚]=>[゜]
[U+309A]=>[゜]
IsolatedVomToNonspace Base character に接続されない独立した濁点と半濁点を Non-space (Combining character) にする。
【例】
[゛]=>[U+3099]
[゙]=>[U+3099]
[゜]=>[U+309A]
[゚]=>[U+309A]

どのように実装したのか?

The Unicode Consortium が提供する UCD in XML (Unicode Character Database in XML) を元に Golang 用のテーブルを生成しています。

ジェネレータのコード
ジェネレータが生成したテーブル (unichar_tables.go)

コード生成の大まかな流れは以下のとおりです。

コード生成シーケンス

No. プロセス 補足
1. zip 形式ファイルのダウンロード
2. zip 解凍、XML ファイル (UCD) の取り出し
3. XML ファイルのパース
4. パースした XML を CSV として出力 確認用 Utility
(ソースコード生成とは別のコマンド)
5. UCD に足りない情報の付与 追加した情報
- ひらがな-カタカナのリレーション、
- 全角小書き文字-半角並字のリレーションなど
6. 文字間の双方向リンクの作成 UCD のひらがなカタカナ情報には片方向のリンクしかない。
- Narrow->Wide(ア->ア など)
- Precomp->Base,Combining (ガ->カ,◌゙ など)
※ラテン文字の Uppercase と Lowercase については双方向のリンク情報がある
7. 拡張 UCD (5, 6 を施したもの)を CSV 出力 確認用 Utility
(ソースコード生成とは別のコマンド)
多言語ライブラリへの移植用としての活用も想定
(Rust, TypeScript/JavaScript, etc...)
8. 拡張 UCD を Golang のテーブルとして出力 with go fmt

なお、コード生成については以下の情報を参考にさせていただきました。

yaegashi さんの記事は、ジェネレータを含むプロジェクトのリポジトリ構成をどうすべきかという点について参考にさせていただきました。

mattn さんの go-runewidth からも多くの事を学ばせていただきました。中でも生成したテーブル構造のチェックサムをテストするアイデアを真似させていただき、これがとても気に入ってます。これを行うことで、万一、元ネタ(ダウンロードデータ)の内容が変更されたり、ジェネレータをリファクタリングした際にうっかり生成コードを壊してしまうようなことがあってもすぐ気づくことができます。

また、GaGa に同梱した 縦書きコマンド vert の中で go-runewidth を使わせていただいています。

お二人ともありがとうございました。

パフォーマンスは良いのか?

パフォーマンスは悪くないと思います。
というか、VOM の結合と分解をしているところ以外は、アルゴリズム的にほぼ何もしていません(笑)

Unicode のブロックごとに 4 つのテーブル (配列) があり(連続するブロックは 1 つにまとめています)、変換する文字がこれのどれにあたるのかを線形検索しているところの平均計算量が O(2)
テーブルが決まってからは、変換する文字のコードポイント(rune の値)からオフセット値を計算、それを配列のインデックスにして要素を取り出しているところの計算量が O(1) です。

ベンチマークのコードと結果は以下のとおりです。

benchmark
const normSTR = "Aa# Aa#あア。ア。”゙漢字ガギグゲゴパピプペポ"

func BenchmarkString(b *testing.B) {
        b.ResetTimer()
        n, _ := Norm(LatinToNarrow | KanaToWide)
        for i := 0; i < b.N; i++ {
                n.String(normSTR)
        }
        b.StopTimer()
}

func BenchmarkNormNFKD(b *testing.B) {
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
                norm.NFKD.String(normSTR)
        }
        b.StopTimer()
}

func BenchmarkNormNFKC(b *testing.B) {
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
                norm.NFKC.String(normSTR)
        }
        b.StopTimer()
}

func BenchmarkWidthNarrow(b *testing.B) {
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
                width.Narrow.String(normSTR)
        }
        b.StopTimer()
}

func BenchmarkWidthWiden(b *testing.B) {
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
                width.Widen.String(normSTR)
        }
        b.StopTimer()
}

func BenchmarkWidthFold(b *testing.B) {
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
                width.Fold.String(normSTR)
        }
        b.StopTimer()
}
benchmark_result
$ make bench
go test -v -run "Benchmark" -bench . -benchmem -o ./output/bench.bin -cpuprofile=./output/cpu.prof -memprofile=./output/mem.prof
goos: linux
goarch: amd64
pkg: github.com/y-bash/go-gaga
BenchmarkString
BenchmarkString-2                 713604              1683 ns/op             224 B/op          2 allocs/op
BenchmarkNormNFKD
BenchmarkNormNFKD-2               189483              6348 ns/op             784 B/op          3 allocs/op
BenchmarkNormNFKC
BenchmarkNormNFKC-2               165166              7101 ns/op             752 B/op          3 allocs/op
BenchmarkWidthNarrow
BenchmarkWidthNarrow-2           1395560               892 ns/op             368 B/op          3 allocs/op
BenchmarkWidthWiden
BenchmarkWidthWiden-2            1392997               854 ns/op             384 B/op          3 allocs/op
BenchmarkWidthFold
BenchmarkWidthFold-2             1452519               822 ns/op             368 B/op          3 allocs/op

上記結果を gaga を 100 として比較すると以下のようになります。

パッケージ 処理時間 メモリ使用量 アロケーション回数
gaga 100 100 100
norm.NFKD 377 350 150
norm.NFKC 422 336 150
width.Narrow 53 164 150
width.Widen 51 171 150
width.Fold 49 164 150

norm パッケージの 3 〜 4 倍の速度が出ているので悪くないと思います。
width は速いですね。VOMの結合と分解をしていないからですかね …

GaGa がサポートする 462文字

ライブラリがサポートする文字の一覧表を挙げておきます。

一覧表を展開する
Block Name Age Codepoint Glyph
ASCII SPACE 1.1 U+0020
ASCII EXCLAMATION MARK 1.1 U+0021 !
ASCII QUOTATION MARK 1.1 U+0022 "
ASCII NUMBER SIGN 1.1 U+0023 #
ASCII DOLLAR SIGN 1.1 U+0024 $
ASCII PERCENT SIGN 1.1 U+0025 %
ASCII AMPERSAND 1.1 U+0026 &
ASCII APOSTROPHE 1.1 U+0027 '
ASCII LEFT PARENTHESIS 1.1 U+0028 (
ASCII RIGHT PARENTHESIS 1.1 U+0029 )
ASCII ASTERISK 1.1 U+002A *
ASCII PLUS SIGN 1.1 U+002B +
ASCII COMMA 1.1 U+002C ,
ASCII HYPHEN-MINUS 1.1 U+002D -
ASCII FULL STOP 1.1 U+002E .
ASCII SOLIDUS 1.1 U+002F /
ASCII DIGIT ZERO 1.1 U+0030 0
ASCII DIGIT ONE 1.1 U+0031 1
ASCII DIGIT TWO 1.1 U+0032 2
ASCII DIGIT THREE 1.1 U+0033 3
ASCII DIGIT FOUR 1.1 U+0034 4
ASCII DIGIT FIVE 1.1 U+0035 5
ASCII DIGIT SIX 1.1 U+0036 6
ASCII DIGIT SEVEN 1.1 U+0037 7
ASCII DIGIT EIGHT 1.1 U+0038 8
ASCII DIGIT NINE 1.1 U+0039 9
ASCII COLON 1.1 U+003A :
ASCII SEMICOLON 1.1 U+003B ;
ASCII LESS-THAN SIGN 1.1 U+003C <
ASCII EQUALS SIGN 1.1 U+003D =
ASCII GREATER-THAN SIGN 1.1 U+003E >
ASCII QUESTION MARK 1.1 U+003F ?
ASCII COMMERCIAL AT 1.1 U+0040 @
ASCII LATIN CAPITAL LETTER A 1.1 U+0041 A
ASCII LATIN CAPITAL LETTER B 1.1 U+0042 B
ASCII LATIN CAPITAL LETTER C 1.1 U+0043 C
ASCII LATIN CAPITAL LETTER D 1.1 U+0044 D
ASCII LATIN CAPITAL LETTER E 1.1 U+0045 E
ASCII LATIN CAPITAL LETTER F 1.1 U+0046 F
ASCII LATIN CAPITAL LETTER G 1.1 U+0047 G
ASCII LATIN CAPITAL LETTER H 1.1 U+0048 H
ASCII LATIN CAPITAL LETTER I 1.1 U+0049 I
ASCII LATIN CAPITAL LETTER J 1.1 U+004A J
ASCII LATIN CAPITAL LETTER K 1.1 U+004B K
ASCII LATIN CAPITAL LETTER L 1.1 U+004C L
ASCII LATIN CAPITAL LETTER M 1.1 U+004D M
ASCII LATIN CAPITAL LETTER N 1.1 U+004E N
ASCII LATIN CAPITAL LETTER O 1.1 U+004F O
ASCII LATIN CAPITAL LETTER P 1.1 U+0050 P
ASCII LATIN CAPITAL LETTER Q 1.1 U+0051 Q
ASCII LATIN CAPITAL LETTER R 1.1 U+0052 R
ASCII LATIN CAPITAL LETTER S 1.1 U+0053 S
ASCII LATIN CAPITAL LETTER T 1.1 U+0054 T
ASCII LATIN CAPITAL LETTER U 1.1 U+0055 U
ASCII LATIN CAPITAL LETTER V 1.1 U+0056 V
ASCII LATIN CAPITAL LETTER W 1.1 U+0057 W
ASCII LATIN CAPITAL LETTER X 1.1 U+0058 X
ASCII LATIN CAPITAL LETTER Y 1.1 U+0059 Y
ASCII LATIN CAPITAL LETTER Z 1.1 U+005A Z
ASCII LEFT SQUARE BRACKET 1.1 U+005B [
ASCII REVERSE SOLIDUS 1.1 U+005C \
ASCII RIGHT SQUARE BRACKET 1.1 U+005D ]
ASCII CIRCUMFLEX ACCENT 1.1 U+005E ^
ASCII LOW LINE 1.1 U+005F _
ASCII GRAVE ACCENT 1.1 U+0060 `
ASCII LATIN SMALL LETTER A 1.1 U+0061 a
ASCII LATIN SMALL LETTER B 1.1 U+0062 b
ASCII LATIN SMALL LETTER C 1.1 U+0063 c
ASCII LATIN SMALL LETTER D 1.1 U+0064 d
ASCII LATIN SMALL LETTER E 1.1 U+0065 e
ASCII LATIN SMALL LETTER F 1.1 U+0066 f
ASCII LATIN SMALL LETTER G 1.1 U+0067 g
ASCII LATIN SMALL LETTER H 1.1 U+0068 h
ASCII LATIN SMALL LETTER I 1.1 U+0069 i
ASCII LATIN SMALL LETTER J 1.1 U+006A j
ASCII LATIN SMALL LETTER K 1.1 U+006B k
ASCII LATIN SMALL LETTER L 1.1 U+006C l
ASCII LATIN SMALL LETTER M 1.1 U+006D m
ASCII LATIN SMALL LETTER N 1.1 U+006E n
ASCII LATIN SMALL LETTER O 1.1 U+006F o
ASCII LATIN SMALL LETTER P 1.1 U+0070 p
ASCII LATIN SMALL LETTER Q 1.1 U+0071 q
ASCII LATIN SMALL LETTER R 1.1 U+0072 r
ASCII LATIN SMALL LETTER S 1.1 U+0073 s
ASCII LATIN SMALL LETTER T 1.1 U+0074 t
ASCII LATIN SMALL LETTER U 1.1 U+0075 u
ASCII LATIN SMALL LETTER V 1.1 U+0076 v
ASCII LATIN SMALL LETTER W 1.1 U+0077 w
ASCII LATIN SMALL LETTER X 1.1 U+0078 x
ASCII LATIN SMALL LETTER Y 1.1 U+0079 y
ASCII LATIN SMALL LETTER Z 1.1 U+007A z
ASCII LEFT CURLY BRACKET 1.1 U+007B {
ASCII VERTICAL LINE 1.1 U+007C
ASCII RIGHT CURLY BRACKET 1.1 U+007D }
ASCII TILDE 1.1 U+007E ~
CJK_Symbols IDEOGRAPHIC SPACE 1.1 U+3000  
CJK_Symbols IDEOGRAPHIC COMMA 1.1 U+3001
CJK_Symbols IDEOGRAPHIC FULL STOP 1.1 U+3002
CJK_Symbols LEFT CORNER BRACKET 1.1 U+300C
CJK_Symbols RIGHT CORNER BRACKET 1.1 U+300D
Hiragana HIRAGANA LETTER SMALL A 1.1 U+3041
Hiragana HIRAGANA LETTER A 1.1 U+3042
Hiragana HIRAGANA LETTER SMALL I 1.1 U+3043
Hiragana HIRAGANA LETTER I 1.1 U+3044
Hiragana HIRAGANA LETTER SMALL U 1.1 U+3045
Hiragana HIRAGANA LETTER U 1.1 U+3046
Hiragana HIRAGANA LETTER SMALL E 1.1 U+3047
Hiragana HIRAGANA LETTER E 1.1 U+3048
Hiragana HIRAGANA LETTER SMALL O 1.1 U+3049
Hiragana HIRAGANA LETTER O 1.1 U+304A
Hiragana HIRAGANA LETTER KA 1.1 U+304B
Hiragana HIRAGANA LETTER GA 1.1 U+304C
Hiragana HIRAGANA LETTER KI 1.1 U+304D
Hiragana HIRAGANA LETTER GI 1.1 U+304E
Hiragana HIRAGANA LETTER KU 1.1 U+304F
Hiragana HIRAGANA LETTER GU 1.1 U+3050
Hiragana HIRAGANA LETTER KE 1.1 U+3051
Hiragana HIRAGANA LETTER GE 1.1 U+3052
Hiragana HIRAGANA LETTER KO 1.1 U+3053
Hiragana HIRAGANA LETTER GO 1.1 U+3054
Hiragana HIRAGANA LETTER SA 1.1 U+3055
Hiragana HIRAGANA LETTER ZA 1.1 U+3056
Hiragana HIRAGANA LETTER SI 1.1 U+3057
Hiragana HIRAGANA LETTER ZI 1.1 U+3058
Hiragana HIRAGANA LETTER SU 1.1 U+3059
Hiragana HIRAGANA LETTER ZU 1.1 U+305A
Hiragana HIRAGANA LETTER SE 1.1 U+305B
Hiragana HIRAGANA LETTER ZE 1.1 U+305C
Hiragana HIRAGANA LETTER SO 1.1 U+305D
Hiragana HIRAGANA LETTER ZO 1.1 U+305E
Hiragana HIRAGANA LETTER TA 1.1 U+305F
Hiragana HIRAGANA LETTER DA 1.1 U+3060
Hiragana HIRAGANA LETTER TI 1.1 U+3061
Hiragana HIRAGANA LETTER DI 1.1 U+3062
Hiragana HIRAGANA LETTER SMALL TU 1.1 U+3063
Hiragana HIRAGANA LETTER TU 1.1 U+3064
Hiragana HIRAGANA LETTER DU 1.1 U+3065
Hiragana HIRAGANA LETTER TE 1.1 U+3066
Hiragana HIRAGANA LETTER DE 1.1 U+3067
Hiragana HIRAGANA LETTER TO 1.1 U+3068
Hiragana HIRAGANA LETTER DO 1.1 U+3069
Hiragana HIRAGANA LETTER NA 1.1 U+306A
Hiragana HIRAGANA LETTER NI 1.1 U+306B
Hiragana HIRAGANA LETTER NU 1.1 U+306C
Hiragana HIRAGANA LETTER NE 1.1 U+306D
Hiragana HIRAGANA LETTER NO 1.1 U+306E
Hiragana HIRAGANA LETTER HA 1.1 U+306F
Hiragana HIRAGANA LETTER BA 1.1 U+3070
Hiragana HIRAGANA LETTER PA 1.1 U+3071
Hiragana HIRAGANA LETTER HI 1.1 U+3072
Hiragana HIRAGANA LETTER BI 1.1 U+3073
Hiragana HIRAGANA LETTER PI 1.1 U+3074
Hiragana HIRAGANA LETTER HU 1.1 U+3075
Hiragana HIRAGANA LETTER BU 1.1 U+3076
Hiragana HIRAGANA LETTER PU 1.1 U+3077
Hiragana HIRAGANA LETTER HE 1.1 U+3078
Hiragana HIRAGANA LETTER BE 1.1 U+3079
Hiragana HIRAGANA LETTER PE 1.1 U+307A
Hiragana HIRAGANA LETTER HO 1.1 U+307B
Hiragana HIRAGANA LETTER BO 1.1 U+307C
Hiragana HIRAGANA LETTER PO 1.1 U+307D
Hiragana HIRAGANA LETTER MA 1.1 U+307E
Hiragana HIRAGANA LETTER MI 1.1 U+307F
Hiragana HIRAGANA LETTER MU 1.1 U+3080
Hiragana HIRAGANA LETTER ME 1.1 U+3081
Hiragana HIRAGANA LETTER MO 1.1 U+3082
Hiragana HIRAGANA LETTER SMALL YA 1.1 U+3083
Hiragana HIRAGANA LETTER YA 1.1 U+3084
Hiragana HIRAGANA LETTER SMALL YU 1.1 U+3085
Hiragana HIRAGANA LETTER YU 1.1 U+3086
Hiragana HIRAGANA LETTER SMALL YO 1.1 U+3087
Hiragana HIRAGANA LETTER YO 1.1 U+3088
Hiragana HIRAGANA LETTER RA 1.1 U+3089
Hiragana HIRAGANA LETTER RI 1.1 U+308A
Hiragana HIRAGANA LETTER RU 1.1 U+308B
Hiragana HIRAGANA LETTER RE 1.1 U+308C
Hiragana HIRAGANA LETTER RO 1.1 U+308D
Hiragana HIRAGANA LETTER SMALL WA 1.1 U+308E
Hiragana HIRAGANA LETTER WA 1.1 U+308F
Hiragana HIRAGANA LETTER WI 1.1 U+3090
Hiragana HIRAGANA LETTER WE 1.1 U+3091
Hiragana HIRAGANA LETTER WO 1.1 U+3092
Hiragana HIRAGANA LETTER N 1.1 U+3093
Hiragana HIRAGANA LETTER VU 1.1 U+3094
Hiragana HIRAGANA LETTER SMALL KA 3.2 U+3095
Hiragana HIRAGANA LETTER SMALL KE 3.2 U+3096
Hiragana COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK 1.1 U+3099
Hiragana COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK 1.1 U+309A
Hiragana KATAKANA-HIRAGANA VOICED SOUND MARK 1.1 U+309B
Hiragana KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK 1.1 U+309C
Hiragana HIRAGANA ITERATION MARK 1.1 U+309D
Hiragana HIRAGANA VOICED ITERATION MARK 1.1 U+309E
Hiragana HIRAGANA DIGRAPH YORI 3.2 U+309F
Katakana KATAKANA-HIRAGANA DOUBLE HYPHEN 3.2 U+30A0
Katakana KATAKANA LETTER SMALL A 1.1 U+30A1
Katakana KATAKANA LETTER A 1.1 U+30A2
Katakana KATAKANA LETTER SMALL I 1.1 U+30A3
Katakana KATAKANA LETTER I 1.1 U+30A4
Katakana KATAKANA LETTER SMALL U 1.1 U+30A5
Katakana KATAKANA LETTER U 1.1 U+30A6
Katakana KATAKANA LETTER SMALL E 1.1 U+30A7
Katakana KATAKANA LETTER E 1.1 U+30A8
Katakana KATAKANA LETTER SMALL O 1.1 U+30A9
Katakana KATAKANA LETTER O 1.1 U+30AA
Katakana KATAKANA LETTER KA 1.1 U+30AB
Katakana KATAKANA LETTER GA 1.1 U+30AC
Katakana KATAKANA LETTER KI 1.1 U+30AD
Katakana KATAKANA LETTER GI 1.1 U+30AE
Katakana KATAKANA LETTER KU 1.1 U+30AF
Katakana KATAKANA LETTER GU 1.1 U+30B0
Katakana KATAKANA LETTER KE 1.1 U+30B1
Katakana KATAKANA LETTER GE 1.1 U+30B2
Katakana KATAKANA LETTER KO 1.1 U+30B3
Katakana KATAKANA LETTER GO 1.1 U+30B4
Katakana KATAKANA LETTER SA 1.1 U+30B5
Katakana KATAKANA LETTER ZA 1.1 U+30B6
Katakana KATAKANA LETTER SI 1.1 U+30B7
Katakana KATAKANA LETTER ZI 1.1 U+30B8
Katakana KATAKANA LETTER SU 1.1 U+30B9
Katakana KATAKANA LETTER ZU 1.1 U+30BA
Katakana KATAKANA LETTER SE 1.1 U+30BB
Katakana KATAKANA LETTER ZE 1.1 U+30BC
Katakana KATAKANA LETTER SO 1.1 U+30BD
Katakana KATAKANA LETTER ZO 1.1 U+30BE
Katakana KATAKANA LETTER TA 1.1 U+30BF
Katakana KATAKANA LETTER DA 1.1 U+30C0
Katakana KATAKANA LETTER TI 1.1 U+30C1
Katakana KATAKANA LETTER DI 1.1 U+30C2
Katakana KATAKANA LETTER SMALL TU 1.1 U+30C3
Katakana KATAKANA LETTER TU 1.1 U+30C4
Katakana KATAKANA LETTER DU 1.1 U+30C5
Katakana KATAKANA LETTER TE 1.1 U+30C6
Katakana KATAKANA LETTER DE 1.1 U+30C7
Katakana KATAKANA LETTER TO 1.1 U+30C8
Katakana KATAKANA LETTER DO 1.1 U+30C9
Katakana KATAKANA LETTER NA 1.1 U+30CA
Katakana KATAKANA LETTER NI 1.1 U+30CB
Katakana KATAKANA LETTER NU 1.1 U+30CC
Katakana KATAKANA LETTER NE 1.1 U+30CD
Katakana KATAKANA LETTER NO 1.1 U+30CE
Katakana KATAKANA LETTER HA 1.1 U+30CF
Katakana KATAKANA LETTER BA 1.1 U+30D0
Katakana KATAKANA LETTER PA 1.1 U+30D1
Katakana KATAKANA LETTER HI 1.1 U+30D2
Katakana KATAKANA LETTER BI 1.1 U+30D3
Katakana KATAKANA LETTER PI 1.1 U+30D4
Katakana KATAKANA LETTER HU 1.1 U+30D5
Katakana KATAKANA LETTER BU 1.1 U+30D6
Katakana KATAKANA LETTER PU 1.1 U+30D7
Katakana KATAKANA LETTER HE 1.1 U+30D8
Katakana KATAKANA LETTER BE 1.1 U+30D9
Katakana KATAKANA LETTER PE 1.1 U+30DA
Katakana KATAKANA LETTER HO 1.1 U+30DB
Katakana KATAKANA LETTER BO 1.1 U+30DC
Katakana KATAKANA LETTER PO 1.1 U+30DD
Katakana KATAKANA LETTER MA 1.1 U+30DE
Katakana KATAKANA LETTER MI 1.1 U+30DF
Katakana KATAKANA LETTER MU 1.1 U+30E0
Katakana KATAKANA LETTER ME 1.1 U+30E1
Katakana KATAKANA LETTER MO 1.1 U+30E2
Katakana KATAKANA LETTER SMALL YA 1.1 U+30E3
Katakana KATAKANA LETTER YA 1.1 U+30E4
Katakana KATAKANA LETTER SMALL YU 1.1 U+30E5
Katakana KATAKANA LETTER YU 1.1 U+30E6
Katakana KATAKANA LETTER SMALL YO 1.1 U+30E7
Katakana KATAKANA LETTER YO 1.1 U+30E8
Katakana KATAKANA LETTER RA 1.1 U+30E9
Katakana KATAKANA LETTER RI 1.1 U+30EA
Katakana KATAKANA LETTER RU 1.1 U+30EB
Katakana KATAKANA LETTER RE 1.1 U+30EC
Katakana KATAKANA LETTER RO 1.1 U+30ED
Katakana KATAKANA LETTER SMALL WA 1.1 U+30EE
Katakana KATAKANA LETTER WA 1.1 U+30EF
Katakana KATAKANA LETTER WI 1.1 U+30F0
Katakana KATAKANA LETTER WE 1.1 U+30F1
Katakana KATAKANA LETTER WO 1.1 U+30F2
Katakana KATAKANA LETTER N 1.1 U+30F3
Katakana KATAKANA LETTER VU 1.1 U+30F4
Katakana KATAKANA LETTER SMALL KA 1.1 U+30F5
Katakana KATAKANA LETTER SMALL KE 1.1 U+30F6
Katakana KATAKANA LETTER VA 1.1 U+30F7
Katakana KATAKANA LETTER VI 1.1 U+30F8
Katakana KATAKANA LETTER VE 1.1 U+30F9
Katakana KATAKANA LETTER VO 1.1 U+30FA
Katakana KATAKANA MIDDLE DOT 1.1 U+30FB
Katakana KATAKANA-HIRAGANA PROLONGED SOUND MARK 1.1 U+30FC
Katakana KATAKANA ITERATION MARK 1.1 U+30FD
Katakana KATAKANA VOICED ITERATION MARK 1.1 U+30FE
Katakana KATAKANA DIGRAPH KOTO 3.2 U+30FF
Katakana_Ext KATAKANA LETTER SMALL KU 3.2 U+31F0
Katakana_Ext KATAKANA LETTER SMALL SI 3.2 U+31F1
Katakana_Ext KATAKANA LETTER SMALL SU 3.2 U+31F2
Katakana_Ext KATAKANA LETTER SMALL TO 3.2 U+31F3
Katakana_Ext KATAKANA LETTER SMALL NU 3.2 U+31F4
Katakana_Ext KATAKANA LETTER SMALL HA 3.2 U+31F5
Katakana_Ext KATAKANA LETTER SMALL HI 3.2 U+31F6
Katakana_Ext KATAKANA LETTER SMALL HU 3.2 U+31F7
Katakana_Ext KATAKANA LETTER SMALL HE 3.2 U+31F8
Katakana_Ext KATAKANA LETTER SMALL HO 3.2 U+31F9
Katakana_Ext KATAKANA LETTER SMALL MU 3.2 U+31FA
Katakana_Ext KATAKANA LETTER SMALL RA 3.2 U+31FB
Katakana_Ext KATAKANA LETTER SMALL RI 3.2 U+31FC
Katakana_Ext KATAKANA LETTER SMALL RU 3.2 U+31FD
Katakana_Ext KATAKANA LETTER SMALL RE 3.2 U+31FE
Katakana_Ext KATAKANA LETTER SMALL RO 3.2 U+31FF
Half_And_Full_Forms FULLWIDTH EXCLAMATION MARK 1.1 U+FF01
Half_And_Full_Forms FULLWIDTH QUOTATION MARK 1.1 U+FF02
Half_And_Full_Forms FULLWIDTH NUMBER SIGN 1.1 U+FF03
Half_And_Full_Forms FULLWIDTH DOLLAR SIGN 1.1 U+FF04
Half_And_Full_Forms FULLWIDTH PERCENT SIGN 1.1 U+FF05
Half_And_Full_Forms FULLWIDTH AMPERSAND 1.1 U+FF06
Half_And_Full_Forms FULLWIDTH APOSTROPHE 1.1 U+FF07
Half_And_Full_Forms FULLWIDTH LEFT PARENTHESIS 1.1 U+FF08
Half_And_Full_Forms FULLWIDTH RIGHT PARENTHESIS 1.1 U+FF09
Half_And_Full_Forms FULLWIDTH ASTERISK 1.1 U+FF0A
Half_And_Full_Forms FULLWIDTH PLUS SIGN 1.1 U+FF0B
Half_And_Full_Forms FULLWIDTH COMMA 1.1 U+FF0C
Half_And_Full_Forms FULLWIDTH HYPHEN-MINUS 1.1 U+FF0D
Half_And_Full_Forms FULLWIDTH FULL STOP 1.1 U+FF0E
Half_And_Full_Forms FULLWIDTH SOLIDUS 1.1 U+FF0F
Half_And_Full_Forms FULLWIDTH DIGIT ZERO 1.1 U+FF10
Half_And_Full_Forms FULLWIDTH DIGIT ONE 1.1 U+FF11
Half_And_Full_Forms FULLWIDTH DIGIT TWO 1.1 U+FF12
Half_And_Full_Forms FULLWIDTH DIGIT THREE 1.1 U+FF13
Half_And_Full_Forms FULLWIDTH DIGIT FOUR 1.1 U+FF14
Half_And_Full_Forms FULLWIDTH DIGIT FIVE 1.1 U+FF15
Half_And_Full_Forms FULLWIDTH DIGIT SIX 1.1 U+FF16
Half_And_Full_Forms FULLWIDTH DIGIT SEVEN 1.1 U+FF17
Half_And_Full_Forms FULLWIDTH DIGIT EIGHT 1.1 U+FF18
Half_And_Full_Forms FULLWIDTH DIGIT NINE 1.1 U+FF19
Half_And_Full_Forms FULLWIDTH COLON 1.1 U+FF1A
Half_And_Full_Forms FULLWIDTH SEMICOLON 1.1 U+FF1B
Half_And_Full_Forms FULLWIDTH LESS-THAN SIGN 1.1 U+FF1C
Half_And_Full_Forms FULLWIDTH EQUALS SIGN 1.1 U+FF1D
Half_And_Full_Forms FULLWIDTH GREATER-THAN SIGN 1.1 U+FF1E
Half_And_Full_Forms FULLWIDTH QUESTION MARK 1.1 U+FF1F
Half_And_Full_Forms FULLWIDTH COMMERCIAL AT 1.1 U+FF20
Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER A 1.1 U+FF21
Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER B 1.1 U+FF22
Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER C 1.1 U+FF23
Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER D 1.1 U+FF24
Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER E 1.1 U+FF25
Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER F 1.1 U+FF26
Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER G 1.1 U+FF27
Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER H 1.1 U+FF28
Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER I 1.1 U+FF29
Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER J 1.1 U+FF2A
Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER K 1.1 U+FF2B
Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER L 1.1 U+FF2C
Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER M 1.1 U+FF2D
Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER N 1.1 U+FF2E
Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER O 1.1 U+FF2F
Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER P 1.1 U+FF30
Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER Q 1.1 U+FF31
Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER R 1.1 U+FF32
Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER S 1.1 U+FF33
Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER T 1.1 U+FF34
Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER U 1.1 U+FF35
Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER V 1.1 U+FF36
Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER W 1.1 U+FF37
Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER X 1.1 U+FF38
Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER Y 1.1 U+FF39
Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER Z 1.1 U+FF3A
Half_And_Full_Forms FULLWIDTH LEFT SQUARE BRACKET 1.1 U+FF3B
Half_And_Full_Forms FULLWIDTH REVERSE SOLIDUS 1.1 U+FF3C
Half_And_Full_Forms FULLWIDTH RIGHT SQUARE BRACKET 1.1 U+FF3D
Half_And_Full_Forms FULLWIDTH CIRCUMFLEX ACCENT 1.1 U+FF3E
Half_And_Full_Forms FULLWIDTH LOW LINE 1.1 U+FF3F _
Half_And_Full_Forms FULLWIDTH GRAVE ACCENT 1.1 U+FF40
Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER A 1.1 U+FF41
Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER B 1.1 U+FF42
Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER C 1.1 U+FF43
Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER D 1.1 U+FF44
Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER E 1.1 U+FF45
Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER F 1.1 U+FF46
Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER G 1.1 U+FF47
Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER H 1.1 U+FF48
Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER I 1.1 U+FF49
Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER J 1.1 U+FF4A
Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER K 1.1 U+FF4B
Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER L 1.1 U+FF4C
Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER M 1.1 U+FF4D
Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER N 1.1 U+FF4E
Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER O 1.1 U+FF4F
Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER P 1.1 U+FF50
Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER Q 1.1 U+FF51
Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER R 1.1 U+FF52
Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER S 1.1 U+FF53
Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER T 1.1 U+FF54
Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER U 1.1 U+FF55
Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER V 1.1 U+FF56
Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER W 1.1 U+FF57
Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER X 1.1 U+FF58
Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER Y 1.1 U+FF59
Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER Z 1.1 U+FF5A
Half_And_Full_Forms FULLWIDTH LEFT CURLY BRACKET 1.1 U+FF5B
Half_And_Full_Forms FULLWIDTH VERTICAL LINE 1.1 U+FF5C
Half_And_Full_Forms FULLWIDTH RIGHT CURLY BRACKET 1.1 U+FF5D
Half_And_Full_Forms FULLWIDTH TILDE 1.1 U+FF5E
Half_And_Full_Forms HALFWIDTH IDEOGRAPHIC FULL STOP 1.1 U+FF61
Half_And_Full_Forms HALFWIDTH LEFT CORNER BRACKET 1.1 U+FF62
Half_And_Full_Forms HALFWIDTH RIGHT CORNER BRACKET 1.1 U+FF63
Half_And_Full_Forms HALFWIDTH IDEOGRAPHIC COMMA 1.1 U+FF64
Half_And_Full_Forms HALFWIDTH KATAKANA MIDDLE DOT 1.1 U+FF65
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER WO 1.1 U+FF66
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER SMALL A 1.1 U+FF67
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER SMALL I 1.1 U+FF68
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER SMALL U 1.1 U+FF69
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER SMALL E 1.1 U+FF6A
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER SMALL O 1.1 U+FF6B
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER SMALL YA 1.1 U+FF6C
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER SMALL YU 1.1 U+FF6D
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER SMALL YO 1.1 U+FF6E
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER SMALL TU 1.1 U+FF6F
Half_And_Full_Forms HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK 1.1 U+FF70
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER A 1.1 U+FF71
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER I 1.1 U+FF72
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER U 1.1 U+FF73
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER E 1.1 U+FF74
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER O 1.1 U+FF75
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER KA 1.1 U+FF76
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER KI 1.1 U+FF77
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER KU 1.1 U+FF78
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER KE 1.1 U+FF79
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER KO 1.1 U+FF7A
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER SA 1.1 U+FF7B
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER SI 1.1 U+FF7C
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER SU 1.1 U+FF7D
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER SE 1.1 U+FF7E
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER SO 1.1 U+FF7F ソ
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER TA 1.1 U+FF80
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER TI 1.1 U+FF81
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER TU 1.1 U+FF82
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER TE 1.1 U+FF83
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER TO 1.1 U+FF84
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER NA 1.1 U+FF85
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER NI 1.1 U+FF86
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER NU 1.1 U+FF87
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER NE 1.1 U+FF88
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER NO 1.1 U+FF89
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER HA 1.1 U+FF8A
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER HI 1.1 U+FF8B
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER HU 1.1 U+FF8C
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER HE 1.1 U+FF8D
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER HO 1.1 U+FF8E
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER MA 1.1 U+FF8F
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER MI 1.1 U+FF90
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER MU 1.1 U+FF91
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER ME 1.1 U+FF92
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER MO 1.1 U+FF93
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER YA 1.1 U+FF94
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER YU 1.1 U+FF95
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER YO 1.1 U+FF96
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER RA 1.1 U+FF97
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER RI 1.1 U+FF98
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER RU 1.1 U+FF99
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER RE 1.1 U+FF9A
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER RO 1.1 U+FF9B
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER WA 1.1 U+FF9C
Half_And_Full_Forms HALFWIDTH KATAKANA LETTER N 1.1 U+FF9D
Half_And_Full_Forms HALFWIDTH KATAKANA VOICED SOUND MARK 1.1 U+FF9E
Half_And_Full_Forms HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK 1.1 U+FF9F

おわりに

Unicode Normalization Form って使いづらいなぁと思って、ネットで調べてみても、この仕様に否定的な記事はあまり見つかりませんでした。

みなさん、norm や width をそのまま使ってるんですかね?
それとも、特に難しくもないからみんな独自にライブラリを作ってるのかな?
あ、そもそもノーマライズとかしてないんでしょうか?

それが気になってます。誰か教えてください(笑)

また、Golang を触り始めてからまだ日が浅いので、ライブラリの実装に抜けがあるかもしれません。

  • Go らしくない部分
  • 非効率な箇所
  • バグ

などを見つけたら教えてもらえると嬉しいです。

それでは!

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4