この記事はQiitaの記事をエクスポートしたものです。内容が古くなっている可能性があります。
はじめに
gojaconvというGo用の文字列変換ライブラリを作成しました。 かな文字をローマ字(ヘボン式)に変換します。
import "github.com/kotaroooo0/gojaconv/jaconv" hebon := jaconv.ToHebon("おはよう") fmt.Println(hebon) // Output: ohayo hebon = jaconv.ToHebon("こんにちは") fmt.Println(hebon) // Output: konnichiha
入力に対して、ローマ字に直して処理したい場合に処理するものが見当たらなかったので自作しました。例えば、僕はTwitterBotを作成しており、リプライのテキストをローマ字文字列集合との類似度を計算したい場面に遭遇しました。その場合、リプライのテキストが元々ローマ字であれば問題ないですが、そうとは限らないのでこのライブラリを使いローマ字に変換することで解決しました。
ちなみにテストカバレッジ100%です。
ok github.com/kotaroooo0/gojaconv/jaconv 3.344s coverage: 100.0% of statements
実装
以下のような愚直なマッピングがベースにあります。
type CharHebon struct { Char string Hebon string } func charHebonByIndex(kana string, index int) CharHebon { hebonMap := map[string]string{ "あ": "a", "い": "i", "う": "u", "え": "e", "お": "o", "か": "ka", "き": "ki", "く": "ku", "け": "ke", "こ": "ko", "さ": "sa", "し": "shi", "す": "su", "せ": "se", "そ": "so", "た": "ta", "ち": "chi", "つ": "tsu", "て": "te", "と": "to", "な": "na", "に": "ni", "ぬ": "nu", "ね": "ne", "の": "no", "は": "ha", "ひ": "hi", "ふ": "fu", "へ": "he", "ほ": "ho", "ま": "ma", "み": "mi", "む": "mu", "め": "me", "も": "mo", "や": "ya", "ゆ": "yu", "よ": "yo", "ら": "ra", "り": "ri", "る": "ru", "れ": "re", "ろ": "ro", "わ": "wa", "ゐ": "i", "ゑ": "e", "を": "o", "ぁ": "a", "ぃ": "i", "ぅ": "u", "ぇ": "e", "ぉ": "o", "が": "ga", "ぎ": "gi", "ぐ": "gu", "げ": "ge", "ご": "go", "ざ": "za", "じ": "ji", "ず": "zu", "ぜ": "ze", "ぞ": "zo", "だ": "da", "ぢ": "ji", "づ": "zu", "で": "de", "ど": "do", "ば": "ba", "び": "bi", "ぶ": "bu", "べ": "be", "ぼ": "bo", "ぱ": "pa", "ぴ": "pi", "ぷ": "pu", "ぺ": "pe", "ぽ": "po", "きゃ": "kya", "きゅ": "kyu", "きょ": "kyo", "しゃ": "sha", "しゅ": "shu", "しょ": "sho", "ちゃ": "cha", "ちゅ": "chu", "ちょ": "cho", "ちぇ": "che", "にゃ": "nya", "にゅ": "nyu", "にょ": "nyo", "ひゃ": "hya", "ひゅ": "hyu", "ひょ": "hyo", "みゃ": "mya", "みゅ": "myu", "みょ": "myo", "りゃ": "rya", "りゅ": "ryu", "りょ": "ryo", "ぎゃ": "gya", "ぎゅ": "gyu", "ぎょ": "gyo", "じゃ": "ja", "じゅ": "ju", "じょ": "jo", "びゃ": "bya", "びゅ": "byu", "びょ": "byo", "ぴゃ": "pya", "ぴゅ": "pyu", "ぴょ": "pyo", } var hebon string var char string utfstr := utf8string.NewString(kana) // 2文字ヒットするとき if index+1 < utf8.RuneCountInString(kana) { char = utfstr.Slice(index, index+2) hebon = hebonMap[char] } // 2文字はヒットしないが1文字はヒットするとき if hebon == "" && index < utfstr.RuneCount() { char = utfstr.Slice(index, index+1) hebon = hebonMap[char] } return CharHebon{Char: char, Hebon: hebon} }
次に以下のルールにしたがって、ローマ字を構築していきます。
- 撥音:B、M、Pの前の「ん」は、NではなくMで表記します。
- 促音:子音を重ねて表記します。
- 長音:OやUは記入しません。
func ToHebon(kana string) string { isOmitted := map[string]bool{ "aa": true, "ee": true, "ii": false, // i は連続しても省略しない "oo": true, "ou": true, "uu": true, } var hebon string var lastHebon string i := 0 for { ch := charHebonByIndex(kana, i) if ch.Char == "っ" { // "っち" nextCh := charHebonByIndex(kana, i+1) if nextCh.Hebon != "" { ch.Hebon = "t" } } else if ch.Char == "ん" { // B,M,P の前の "ん" は "M" とする。 nextCh := charHebonByIndex(kana, i+1) if nextCh.Hebon != "" && strings.Index("bmp", nextCh.Hebon[0:1]) != -1 { ch.Hebon = "m" } else { ch.Hebon = "n" } } else if ch.Char == "ー" { // 長音は無視 ch.Hebon = "" } if ch.Hebon != "" { // 変換できる文字の場合 if lastHebon != "" { // 連続する母音の除去 joinedHebon := lastHebon + ch.Hebon if len(joinedHebon) > 2 { joinedHebon = joinedHebon[len(joinedHebon)-2:] } if isOmitted[joinedHebon] { ch.Hebon = "" } } hebon += ch.Hebon } else { if ch.Char != "ー" { // 変換できない文字の場合 hebon += ch.Char } } lastHebon = ch.Hebon i += utf8.RuneCountInString(ch.Char) if i >= utf8.RuneCountInString(kana) { break } } return hebon }