- 投稿日:2020-09-21T23:42:14+09:00
【Golang】Go言語の基礎 結局ポインタって何なの??
Go言語やC言語を学ぶ上で避けては通れないのがポインタです。
プログラミング初学者の入門言語であるRubyやPHPにはポインタという概念がありません。
Rubyから入った僕にはいろんな記事を読んでも???でした!そんな方にもわかるようにここでは解説いたします!ポインタとは?
プログラムというのは、メモリの中で読み込まれ、CPUで処理されます。また、僕たちが普段プログラムで使っている変数というのはメモリの中に配置されます。CPUによってメモリの中に配置されている変数の値を処理しているのです。
ポインタというのは、変数がメモリのどこに配置されているか?という情報(変数の住所)を格納する変数のことです。早速コードを書いてみる
package main import "fmt" func main() { var n int = 10 fmt.Println(n) // => 10 //アンパサンドを付けることで変数を入れたメモリの住所(正式にはアドレスといいます。)が表示できる。 fmt.Println(&n) // => 0xc00002c008 //* を付けることでintgerのポインタ型の変数p(変数nのアドレスを入れる箱)を用意できる。 var p *int = &n fmt.Println(p) // => 0xc00002c008 //変数pの値を表示したいときは*pとすると、値を表示できる。 fmt.Println(*p) // => 10 }var 変数名 データ型 とすることで、変数のアドレスを格納する変数を宣言することができます。
ここに変数のアドレスを入れるためには &変数名とするとその変数のアドレスを格納できます。
格納したアドレスの値を表示したい場合、変数名とすることによって値を表示することができます。もう少し書いてみる。
package main import "fmt" func one(x int) { x = 1 } func main() { var n int = 10 one(n) fmt.Println(n) // => 10 }↑に書いたコードはone関数の引数に値しか渡していないので、最初にmain関数で宣言したxに影響はありません。
実際にxの値を更新するにはxのアドレスを引数に渡す必要があります。このようなことをしたいときにポインタが使われます↓
package main import "fmt" func one(x *int) { *x = 1 } func main() { var n int = 10 one(&n) fmt.Println(n) // => 1 }one関数の引数に&nとしてnのアドレスを渡しています。その際引数はポインタ型でなければアドレスを受け取ることができないので x *int としてポインタ型の引数を定義しています。
これはやっていることとしては最初にやった var p *int = &n とやっていることは同じだとわかります。最後まで読んでいただきありがとうございます!
何かご指摘などございましたらコメント等いただけると嬉しいです!!
- 投稿日:2020-09-21T17:40:24+09:00
JWE(JSON Web Encryption)は2020年現在も危険なのか?
概要
いくつかの記事でJWEは避けるべき、脆弱性がある。という指摘があるが、これらが議論されていたのは2017年頃だ。2020年においてまだ該当記事の脆弱性が実際に存在するか調査した結果を記録する。私はJWEのエキスパートではないし、RFC-7516を全て読み込んでもいないため間違っている部分があるかもしれない。もしこの調査結果に対してフィードバックがあれば編集リクエストを送ってほしい。
結論
現在は脆弱性は修正され、安全なようだ。脆弱性に対応していないライブラリを使っている場合はもちろん危険なので使用しているライブラリが対応されているか調査する必要がある。
調査内容
go-joseの修正
oss-sec: CVE request: multiple issues in go-jose package ここでCVE番号のリクエストと、パッチへのリンクが提供されている。
Critical Vulnerability in JSON Web Encryption? · Issue #141 · square/go-jose これはgo-joseライブラリの質問。
https://github.com/square/go-jose/issues/141#issuecomment-286887765 このコメントでその脆弱性は1.1.0で修正されているよ。と書かれている。
脆弱性の概要
攻撃の名称は、Classic Invalid Curve Attack.というらしい。
この脆弱性にさらされると、攻撃者はJWE with Key Agreement with Elliptic Curve Diffie-Hellman Ephemeral Static (ECDH-ES)を使用して、送信者が受信者の秘密鍵を取り出すことができるようになってしまうよ、ということらしい。
Critical Vulnerability in JSON Web Encryption ここが詳しい。
Auth0のAntonioとIETFチームのやりとり
[jose] Use of ECDH-ES in JWEこのメーリングリストでやり取りしている。
IETFの人もCurve Attackについては言及されていないとしながらも、本文は修正することができないため、Errata(正誤表)に記載してほしいとなり、Antonioが正誤表に投稿する旨で決着していました。ただ、RFC Errata Report » RFC Editorこの正誤表検索で該当のエントリは見つからないため、投稿されていないか、レビューされていない可能性がありそうだ。
JWEの利用実績
- KubernetesのDashboardとブラウザ間の通信で実際に利用されている。Kubernetes Dashboardの該当部分はgo-joseが使われている。
- 投稿日:2020-09-21T17:26:36+09:00
Goで画像の形式を変換するCLIツールを作った
はじめに
Goで画像の形式変換をするCLIツールを作りました。
- jpg (jpeg)
- png
- gif
の間で相互に変換可能です。
機能
オプション 説明 デフォルト -f 変換前の画像の拡張子 .jpeg -t 変換後の画像の拡張子 .png -r 変換前の画像を削除する false 使い方
オプションに何も指定しなかった場合は、.jpeg → .png への変換になります。
$ ./imgconv sample.jpeg.png → .jpeg への変換はこんな感じ。
./imgconv -f .png -t .jpg sample.png変換前の画像を削除したい場合はこんな感じ。
./imgconv -r sample.jpegコード
ディレクトリ構成は以下の通りです。
├── cmd │ └── imgconv │ └── main.go ├── imgconv.go └── go.modまずは、imgconv.goから見ていきます。
説明はコメントに書きます。imgconv.gopackage imgconv import ( "image" "image/gif" "image/jpeg" "image/png" "os" "path/filepath" ) // 変換前のファイルを削除する func removeSrc(src string) error { err := os.Remove(src) if err != nil { return err } return nil } // 画像の拡張子を変換する func Convert(src, dst string, rmsrc bool) error { // 変換前のファイルを読み取り専用で開く sf, err := os.Open(src) if err != nil { return err } defer sf.Close() // image.Imageへとデコード img, _, err := image.Decode(sf) if err != nil { return err } // 読み書き用ファイルを作成 df, err := os.Create(dst) if err != nil { return err } defer df.Close() // .以降を取り出して条件分岐 その後image.Imageからエンコード switch filepath.Ext(dst) { case ".png": err = png.Encode(df, img) case ".jpg", ".jpeg": // 第三引数は画質で1~100まで。(今回は定数を使用:75) err = jpeg.Encode(df, img, &jpeg.Options{Quality: jpeg.DefaultQuality}) case ".gif": err = gif.Encode(df, img, nil) } if err != nil { return err } // 変換前のファイル削除 if rmsrc { if err = removeSrc(src); err != nil { return err } } return nil }次は、cmd/imgconv/main.go
cmd/imgconv/main.gopackage main import ( "flag" "fmt" "os" "strings" "github.com/Le0tk0k/imgconv" ) // 第一引数に名前、第二引数にデフォルト値、第三引数に使い方を渡す。戻り値はフラグの値が格納されるそれぞれの型の変数のアドレス。 var from = flag.String("f", ".jpeg", "Extension before conversion") var to = flag.String("t", ".png", "Extension after conversion") var rm = flag.Bool("r", false, "Remove file before conversion") func main() { flag.Parse() // コマンドライン引数がパースされ、ポインタの指す先に値が設定される。 src := flag.Arg(0) // 第一引数の文字列のコピーに対し、第四引数個、第二引数の部分を第三引数に置換したものを返す。 dst := strings.Replace(flag.Arg(0), *from, *to, 1) err := imgconv.Convert(src, dst, *rm) if err != nil { fmt.Fprintf(os.Stderr, "%s\n", err.Error()) } }flag.Argsはos.Argsと違って0番目の要素にコマンドを含まないので、flag.Arg(0)で変換前のファイル名を取得できます。
以上です!
さいごに
簡単に作れて、標準パッケージの勉強にもなるのでコマンドラインツール作成おすすめです。
- 投稿日:2020-09-21T17:13:22+09:00
ゴゴコ゛ゴゴゴコ゛をゴゴゴゴゴゴゴにするGo言語のライブラリを作ってみた
はじめに
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 種類の「ゴ」について
- Combining characters について
- Unicode 正規化フォームの問題点
- どんなライブラリを作ったのか?
- どのように実装したのか?
- パフォーマンスは良いのか?
- GaGa がサポートする 462文字
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 を含む同一の文字列をフォントだけ変えて表示した例です。編集時の挙動もアプリケーションによって違います。
アプリケーションによって、濁点だけを削除することができたり、先行の文字にくっついて削除できなかったりと、違いがあります。
興味がある人は以下の文字列をエディタなどにコピペして試してみてください。
え゚?゚ 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 ] => [ 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.gopackage 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
FF9EWide VOM(309B) が Narrow にならない。
Wide Precomp(30B4) が Narrow にならない。Widen ゴ
30B3
3099コ゛
30B3
309Bゴ
30B3
3099ゴ
30B4
ゴ
30B3
3099コ゛
30B3
309Bゴ
30B3
30993 つとも Precomp(30B4) になってほしい。
せめて Base + Combining (30B3+3099) に揃えてほしい。
Fold ゴ
30B3
3099コ゛
30B3
309Bゴ
30B3
3099ゴ
30B4
ゴ
30B3
3099コ゛
30B3
309Bゴ
30B3
3099Fold は 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 => PrecompNFKD 互換等価性によって分解される。 半角カタカナが全角カタカナになる。
Narrow => WideNFKC 互換等価性によって分解された後に、正準等価性によって合成される。 半角カタカナが全角カタカナに変換された後に、基底文字と合成文字が合成される。
Narrow => Wide,
Base + Combining => Precomp7 種類の「ゴ」を変換してみます。
norm_examples.gopackage 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
3099Combining ではない 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 に置いてあります。
go-gaga (Japanese language utility)
READMEGolang の環境があれば、以下のコマンドでライブラリが使えるようになります。
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.gopackage 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.gopackage 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
3099ComposeVom ゴ
FF7A
FF9Eゴ
FF7A
FF9Eゴ
FF7A
FF9Eゴ
30B4
ゴ
30B4
ゴ
30B4
ゴ
30B4
DecomposeVom ゴ
FF7A
3099ゴ
FF7A
3099ゴ
FF7A
3099ゴ
30B3
3099ゴ
30B3
3099ゴ
30B3
3099ゴ
30B3
3099KanaToNarrow ゴ
FF7A
FF9Eゴ
FF7A
FF9Eゴ
FF7A
FF9Eゴ
FF7A
FF9Eゴ
FF7A
FF9Eゴ
FF7A
FF9Eゴ
FF7A
FF9EKanaToWide ゴ
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.gopackage 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 さん の記事 - go generate のベストプラクティス
- mattn さん のライブラリ - go-runewidth
yaegashi さんの記事は、ジェネレータを含むプロジェクトのリポジトリ構成をどうすべきかという点について参考にさせていただきました。
mattn さんの go-runewidth からも多くの事を学ばせていただきました。中でも生成したテーブル構造のチェックサムをテストするアイデアを真似させていただき、これがとても気に入ってます。これを行うことで、万一、元ネタ(ダウンロードデータ)の内容が変更されたり、ジェネレータをリファクタリングした際にうっかり生成コードを壊してしまうようなことがあってもすぐ気づくことができます。
また、GaGa に同梱した 縦書きコマンド vert の中で go-runewidth を使わせていただいています。
お二人ともありがとうございました。
パフォーマンスは良いのか?
パフォーマンスは悪くないと思います。
というか、VOM の結合と分解をしているところ以外は、アルゴリズム的にほぼ何もしていません(笑)Unicode のブロックごとに 4 つのテーブル (配列) があり(連続するブロックは 1 つにまとめています)、変換する文字がこれのどれにあたるのかを線形検索しているところの平均計算量が O(2)、
テーブルが決まってからは、変換する文字のコードポイント(rune の値)からオフセット値を計算、それを配列のインデックスにして要素を取り出しているところの計算量が O(1) です。ベンチマークのコードと結果は以下のとおりです。
benchmarkconst 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 0 Half_And_Full_Forms FULLWIDTH DIGIT ONE 1.1 U+FF11 1 Half_And_Full_Forms FULLWIDTH DIGIT TWO 1.1 U+FF12 2 Half_And_Full_Forms FULLWIDTH DIGIT THREE 1.1 U+FF13 3 Half_And_Full_Forms FULLWIDTH DIGIT FOUR 1.1 U+FF14 4 Half_And_Full_Forms FULLWIDTH DIGIT FIVE 1.1 U+FF15 5 Half_And_Full_Forms FULLWIDTH DIGIT SIX 1.1 U+FF16 6 Half_And_Full_Forms FULLWIDTH DIGIT SEVEN 1.1 U+FF17 7 Half_And_Full_Forms FULLWIDTH DIGIT EIGHT 1.1 U+FF18 8 Half_And_Full_Forms FULLWIDTH DIGIT NINE 1.1 U+FF19 9 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 A Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER B 1.1 U+FF22 B Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER C 1.1 U+FF23 C Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER D 1.1 U+FF24 D Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER E 1.1 U+FF25 E Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER F 1.1 U+FF26 F Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER G 1.1 U+FF27 G Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER H 1.1 U+FF28 H Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER I 1.1 U+FF29 I Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER J 1.1 U+FF2A J Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER K 1.1 U+FF2B K Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER L 1.1 U+FF2C L Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER M 1.1 U+FF2D M Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER N 1.1 U+FF2E N Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER O 1.1 U+FF2F O Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER P 1.1 U+FF30 P Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER Q 1.1 U+FF31 Q Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER R 1.1 U+FF32 R Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER S 1.1 U+FF33 S Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER T 1.1 U+FF34 T Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER U 1.1 U+FF35 U Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER V 1.1 U+FF36 V Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER W 1.1 U+FF37 W Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER X 1.1 U+FF38 X Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER Y 1.1 U+FF39 Y Half_And_Full_Forms FULLWIDTH LATIN CAPITAL LETTER Z 1.1 U+FF3A Z 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 a Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER B 1.1 U+FF42 b Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER C 1.1 U+FF43 c Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER D 1.1 U+FF44 d Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER E 1.1 U+FF45 e Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER F 1.1 U+FF46 f Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER G 1.1 U+FF47 g Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER H 1.1 U+FF48 h Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER I 1.1 U+FF49 i Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER J 1.1 U+FF4A j Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER K 1.1 U+FF4B k Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER L 1.1 U+FF4C l Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER M 1.1 U+FF4D m Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER N 1.1 U+FF4E n Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER O 1.1 U+FF4F o Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER P 1.1 U+FF50 p Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER Q 1.1 U+FF51 q Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER R 1.1 U+FF52 r Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER S 1.1 U+FF53 s Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER T 1.1 U+FF54 t Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER U 1.1 U+FF55 u Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER V 1.1 U+FF56 v Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER W 1.1 U+FF57 w Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER X 1.1 U+FF58 x Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER Y 1.1 U+FF59 y Half_And_Full_Forms FULLWIDTH LATIN SMALL LETTER Z 1.1 U+FF5A z 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 らしくない部分
- 非効率な箇所
- バグ
などを見つけたら教えてもらえると嬉しいです。
それでは!
- 投稿日:2020-09-21T16:24:31+09:00
Golandでdelve古くてデバッガ動かねえよって叱られた時の対処
うっすい内容ですがvscodeの情報は結構あったけどGolandのは発見出来なかったので一応
タイトル通り、GolandでDebug実行しようとすると
Version of Delve is too old for this version of Go ...こんなんが出て叱られるときの対処です。
Golandはこの記事書いた時点で1番新しいです単純にgo-delve自前でビルドしてプラグイン差し替えれば良いです。
mainはcmd/dlv/
配下にあるので、git clone
かましたらディレクトリ移動してgo build
念のためもともとあったdlvバイナリはdlv.bk
とかにしておき、
できあがったバイナリを[[your path]]/[[Goland本体ディレクトリ]]/plugins/go/lib/dlv/linux/
下に放り込めばok
- 投稿日:2020-09-21T12:33:26+09:00
書籍「Go言語でつくるインタプリタ」の進行で躓いたところ
はじめに
書籍「Go言語でつくるインタプリタ」を進めていたところ、序盤(1.3節)から躓いたので解決方法をメモしておこうと思います。
なお、go言語の開発で本来とるべき構成などとは異なっている可能性がありますが、あくまでこの書籍をすすめるための対処となりますのでご了承ください。1章3節でビルド結果が違う
1.3節をすすめていると、テストコードのビルドの際に紙面では「New関数が定義されていないのでエラーが発生する」、という段取りのはずが
想定# monkey/lexer [monkey/lexer.test] lexer\lexer_test.go:28:7: undefined: New FAIL monkey/lexer [build failed] FAILテストコードの中で指定している「パッケージ"monkey/token"が見つからない」と出たところで異常に気づきました。
実際lexer\lexer_test.go:6:2: cannot find package "monkey/token" in any of: c:\go\src\monkey\token (from $GOROOT) C:\Users\Hoge\go\src\monkey\token (from $GOPATH) FAIL _/C_/Users/Hoge/source/repos/monkey/lexer [setup failed] FAILそもそもgoのパスを通していなかった。
自作したモジュールを使用してコーディングを進めていくので、そのモジュールまでのパスが通っていなければいけないはずですが、golangのインストールの際に設定される環境変数GOPATHをそのままにしていました。
パスに\srcが付け足される。
エラーメッセージの展開後パスから、プロジェクトのソースコードはsrcフォルダの下にあることが想定されていることがわかりました。(パスとモジュール指定の間に\srcが挿入されて展開されるため、見つからない、というエラーになる)
多分goのリファレンスからきちんと入っていればこのようなことは起こらなかったのでしょうが、当のインタプリタ書籍から入ったので全くわかりませんでした。対処
結論として、以下のようにフォルダ構成と環境変数を設定することで書籍の内容進行に対応しました。
フォルダ構造
以下のような構造に修正しました。
\Users\UserName\source\repos\ monkey\ + src\ + monkey\ (<- 「go test位置」) + lexer\ | + lexer_test.go | + (lexer.go) | + token\ + token.go(筆者はVisual Studioのプロジェクトフォルダの生成場所の法則によせて、\Users\UserName\source\repos\フォルダの下に各プロジェクトのフォルダを作成しています。)
環境変数
環境変数の
GOPATH
に以下を追加しました。%USERPROFILE%\source\repos\monkey # USERPROFILEは \Users\Hogeに展開されるgo test(テスト実行)コマンドを通すには
書籍どおりのテストコード実行を行うには
monkey\
プロジェクトフォルダのsrc\monkey\
にフォルダ移動してからになります。
(上図のフォルダ構成の 「go test位置」)想定通りC:\Users\Hoge\source\repos\monkey\src\monkey>go test ./lexer # monkey/lexer [monkey/lexer.test] lexer\lexer_test.go:28:7: undefined: New FAIL monkey/lexer [build failed] FAILまとめ
以下の様に対処して、「Go言語でつくるインタプリタ」1.3節をいなしました。
- フォルダ構成の見直し
- 環境変数の追加
- go test実行時のフォルダ位置
以上です。
- 投稿日:2020-09-21T11:56:39+09:00
【Golang】コレクションを導入したら幸せになったので、わかりやすく説明してみる
はじめに
こんにちは。むらってぃーです。
チームで開発しているプロダクトのコードがそれなりに大きくなってきました。メソッドによっては「割と冗長になってきたね」という話が出てきました。
その部分のほとんどが配列操作の部分だったので、コレクションを使ってリファクタリングを行ったのがこの記事のきっかけです。エンティティのスライス操作のロジックをコレクションに閉じ込めると、可読性が上がったりテストしやすくなったりで幸せになれたので紹介します。
実装例を交えて紹介
今回は、bookというエンティティと、それを扱うリポジトリ、サービスをコンポーネントとして扱います。
今回扱うコンポーネント
エンティティ
bookというエンティティはID、タイトル、著者の3つの値を持ちます。
book.gopackage entity type Book struct { ID uint64 Title string Author string } func NewBook(id uint64, title, author string) *Book { return &Book{ ID: id, Title: title, Author: author, } }リポジトリ
bookエンティティをDBに入れたり取り出したりします。
今回はDB操作にxormというORMを使います。book_repository.gopackage gateway import ( "github.com/go-xorm/xorm" "go-collection-sample/internal/entity" ) // bookをDBに入れたり取り出したりするリポジトリのインターフェース type IBookRepository interface { FindByAuthor(author string) ([]*entity.Book, error) } type BookRepository struct { engine *xorm.Engine } func NewBookRepository(engine *xorm.Engine) *BookRepository { return &BookRepository{ engine: engine, } } type bookData struct { Id uint64 Title string `xorm:"varchar(100)"` Author string `xorm:"varchar(50)"` } // 著者名でbookを複数件取得 func (br *BookRepository) FindByAuthor(author string) ([]*entity.Book, error) { // DBからAuthorを取ってくる var results []bookData if err := br.engine.Where("author = ?", author).Find(&results); err != nil { return []*entity.Book{}, err } // エンティティのスライスに詰めて返す(重要) var books []*entity.Book for _, result := range results { book := entity.NewBook(result.Id, result.Title, result.Author) books = append(books, book) } return books, nil }サービス
ビジネスロジックにあたる部分です。
ここが今回の本題です。book_service.gopackage service import ( "go-collection-sample/internal/entity" "go-collection-sample/internal/gateway" ) type BookService struct { bookRepository gateway.IBookRepository } func NewBookService(repository gateway.IBookRepository) *BookService { return &BookService{ bookRepository: repository, } }DB操作にリポジトリを使います。
このままでも問題ないパターン
book_service.goに「著者名で本を検索する」というメソッドがあったら、その実装はどうなるでしょうか。
book_service.go// 著者名から本を検索して返す func (b *BookService) SearchBooksByAuthor(author string) ([]*entity.Book, error) { // DBからBookをまとめて取ってくる books, err := b.bookRepository.FindByAuthor(author) if err != nil { return []*entity.Book{}, err } return books, nil }DBからbookをまとめて取ってきて、エンティティを返すだけなので非常にシンプルなロジックになりますね。
問題の実装
では、「著者名で本を検索し、そのID配列を取得する」というメソッドがあったら、その実装はどうなるでしょうか。
そのまま書くと下記の形になるのではないでしょうか。book_service.go// 著者名からbookを検索し、それらのIDをスライスで返す func (b *BookService) SearchBookIDsByAuthor(author string) ([]uint64, error) { // DBからBookをまとめて取ってくる books, err := b.bookRepository.FindByAuthor(author) if err != nil { return []uint64{}, err } // IDのリストに詰め直す(重要) var ids []uint64 for _, book := range books { ids = append(ids, book.ID) } return ids, nil }for文でIDを取り出し、IDに詰め直す記述があることがわかります。
ではここで、「著者名で本を検索し、それらの本をお気に入りに登録しているユーザーを一括取得したい」といったメソッドを生やしたい場合はどうでしょうか。
この場合、上記と同じ「IDのリストに詰め直す」処理を再度書かなければなりません。
そこでコレクションの登場です。
コレクションを導入する
コレクションの定義は、
プログラミングの分野で、データやオブジェクトなどをまとめて格納するためのデータ構造やクラスなどの総称をコレクションということがある。
といった感じです。
参考ページ: IT用語辞典今回は下記のコレクションを用意します。
books.gopackage collection import "go-collection-sample/internal/entity" // bookエンティティを束ねておくコレクション type Books struct { books []*entity.Book } func NewBooks() *Books { return &Books{ books: []*entity.Book{}, } } // コレクションにbookを1つ追加 func (b *Books) Add(book *entity.Book) { b.books = append(b.books, book) } // コレクションに束ねているbookのIDをスライスで取得 func (b *Books) IDs() []uint64 { var ids []uint64 for _, book := range b.books { ids = append(ids, book.ID) } return ids }で、repositoryを下記のように書き換えます。
book_repository.gopackage gateway import ( "github.com/go-xorm/xorm" "go-collection-sample/internal/collection" "go-collection-sample/internal/entity" ) type IBookRepository interface { FindByAuthor(author string) (*collection.Books, error) } type BookRepository struct { engine *xorm.Engine } func NewBookRepository(engine *xorm.Engine) *BookRepository { return &BookRepository{ engine: engine, } } type bookData struct { Id uint64 Title string `xorm:"varchar(100)"` Author string `xorm:"varchar(50)"` } // 著者名で検索し、ヒットしたbookエンティティのコレクションを返す func (br *BookRepository) FindByAuthor(author string) (*collection.Books, error) { // DBからAuthorを取ってくる var results []bookData if err := br.engine.Where("author = ?", author).Find(&results); err != nil { return collection.NewBooks(), err } // bookをコレクションに詰めて返す(変更後) books := collection.NewBooks() for _, result := range results { book := entity.NewBook(result.Id, result.Title, result.Author) books.Add(book) } return books, nil }すると、先ほどのメソッドは下記のように書き換えることができます。
book_service.gofunc (b *BookService) SearchBookIDsByAuthor(author string) ([]uint64, error) { // DBからbookのコレクションを取ってくる books, err := b.bookRepository.FindByAuthor(author) if err != nil { return []uint64{}, err } // コレクションにIDスライス取得の処理を任せる return books.IDs(), nil }非常にシンプルになったのではないでしょうか。
記述量を減らせただけでなく、コレクションの導入によって「複数のエンティティを束ねる責務」を他のオブジェクトに移譲できています。
よって、SearchBookIDsByAuthor
メソッドの中もサッと読んで何をしているのかを理解しやすくなっているのではないでしょうか。サービス側でエンティティのスライスを取り回す場合と、コレクションを取り回す場合を再度比較してみましょうk。
エンティティのスライスを取り回す場合
book_service.go// 著者名からbookを検索し、それらのIDをスライスで返す func (b *BookService) SearchBookIDsByAuthor(author string) ([]uint64, error) { // DBからBookをまとめて取ってくる books, err := b.bookRepository.FindByAuthor(author) if err != nil { return []uint64{}, err } // IDのリストに詰め直す(重要) var ids []uint64 for _, book := range books { ids = append(ids, book.ID) } return ids, nil }コレクションを取り回す場合
book_service.gofunc (b *BookService) SearchBookIDsByAuthor(author string) ([]uint64, error) { // DBからbookのコレクションを取ってくる books, err := b.bookRepository.FindByAuthor(author) if err != nil { return []uint64{}, err } // コレクションにIDスライス取得の処理を任せる return books.IDs(), nil }他のケース
他にも、「サービス内でソートを行いたいとき」にも役立ちます。
RDBだとDBから取ってくる際にソートをかけることができますが、CSVファイルからデータを取得したり、NoSQLを使う場合だとアプリケーション側でソートすることがあります。「著者名で検索し、タイトル名の昇順でbooksのエンティティ配列を返す」というメソッドを例にしてみます。
こちらもサービス側でエンティティのスライスを取り回す場合と、コレクションを取り回す場合を比較してみましょう。
エンティティのスライスを取り回す場合
book_service.go// 著者名で検索し、タイトル名の昇順で返す func (b *BookService) SearchBooksByAuthorOrderByTitleAsc(author string) ([]*entity.Book, error) { // DBからBookをまとめて取ってくる books, err := b.bookRepository.FindByAuthor(author) if err != nil { return []*entity.Book, err } // Titleの昇順にソート sort.Slice(books, func(i, j int) bool { return books[i].Title < books[j].Title }) // エンティティの配列を返す return books, nil }「Titleの昇順にソート」というコメントを書かなかった場合を考えてみましょう。
このコメントがなければ、一度ソートをしている部分で立ち止まって、何をしているか解読するための時間が必要になるのではないでしょうか。コレクションを取り回す場合
コレクションに下記を追加。
books.go// タイトルの昇順にソートする func (b *Books) SortAscByTitle() { sort.Slice(b.books, func(i, j int) bool { return b.books[i].Title < b.books[j].Title }) } // スライスに変換する func (b *Books) Slice() []*entity.Book { var eBooks []*entity.Book for _, book := range b.books { eBooks = append(eBooks, book) } return eBooks }サービスを下記のように書き換えます。
book_service.go// 著者名で検索し、タイトルの昇順で返す func (b *BookService) SearchBooksByAuthorOrderByTitleAsc(author string) ([]*entity.Book, error) { books, err := b.bookRepository.FindByAuthor(author) if err != nil { return []*entity.Book, err } books.SortAscByTitle() return books.Slice(), nil }あえてメソッドの中にコメントを書かずにコードを書いてみました。
コメントがなくとも、「何をしているか」は一目瞭然なのではないでしょうか。ちなみに各処理を解説すると、
- リポジトリからbooksのコレクションを取ってくる
- booksをタイトルの昇順にソート
- booksをスライスに変換して返す
となります。
その他のコレクションメソッド
下記のような、スライスを扱う操作を閉じ込めることができます。
books.go// 空である func (b *Books)IsEmpty() bool { if len(b.books) == 0 { return true } return false } // 順番をシャッフルする func (b *Books) Shuffle() { rand.Seed(time.Now().UnixNano()) rand.Shuffle(len(b.books), func(i, j int) { b.books[i], b.books[j] = b.books[j], b.books[i] }) } // 指定したインデックスの要素を削除する func (b *Books) Delete(index int) { var result []*entity.Book for i, book := range b.books{ if i == index { continue } result = append(result, book) } b.books = result }コレクションを使う利点
私としては下記の利点を感じました。
- スライスを扱う汎用的な操作を共通のメソッドに切り出すことができる
- スライスを扱う処理をメソッドに切り出すことで、メソッドを呼び出す部分に意味を持たせることができる
- スライスのソートといった複雑な処理をテストしやすい
最後に
今回はコレクションを導入することで得られる恩恵と、コレクションの使用例について紹介しました。
コレクションを使う分構造体は増えますし、好みはあるかもしれません。
しかしそれによって受けられる恩恵もまた大きいのではないでしょうか。もしスライス操作のロジックが散らばって可読性が低くなっている場合は、参考にしてみてください。
- 投稿日:2020-09-21T11:14:11+09:00
Emacs で Go の開発パッケージを入れられない Not found, Failed to verify signature の解決方法 (Ubuntu 18.04)
タイトルが長い。
emacsで package-install が上手くできなかった方向けです。
以下の参考のブログの、「必要な go パッケージの導入」まで行っている事が前提です。参考
https://qiita.com/bussorenre/items/3e80e59d517db8aebf46
Emacs のバージョン確認
emacs --versionバージョンが26以上の方は次の作業をする必要はありません。
25以下の方はバージョンアップする必要があります。
(解る方は、鍵を取得して更新する方法もあります)Emacs のバージョンアップ
Emacs の package-install ではバージョンが古いと署名鍵が一致しない場合があるようです。
この場合、パッケージを管理しているレポジトリに接続できません。新しいバージョンのEmacsを使うことでこの問題を解決できます。
sudo add-apt-repository ppa:kelleyk/emacs sudo apt update sudo apt install emacs26emacsの最新版インストールしましょう。
現在27まであるようなので、お好みで。sudo apt remove --autoremove emacs25*古いEmacsを削除します。
emacs25*
に指定するバージョンは、一番最初に確認した値に合わせて変更してください。Init.elをいじる
init.elを書き換えている方は、そのファイルを変更します。
init.el自体書いた覚えのない人は、以下に新たにファイルを作ります。cd ~/.emacs.d/ vim init.el先頭に以下の文を追加します。
既に書かれていた場合は、この作業は不要です。(package-initialize) (setq package-archives '(("gnu" . "http://elpa.gnu.org/packages/") ("melpa" . "http://melpa.org/packages/") ("org" . "http://orgmode.org/elpa/")))今回導入するパッケージは、melpaと呼ばれるリポジトリに登録されています。
デフォルトでは接続しないため、接続先として設定してあげる必要がありました。package-install
パッケージリストを更新しておきましょう。
Emacsを起動して以下を実行します。M-x package-refresh-contentsM-xが何かわからない方は、取り合えず
Alt+x
を押すと良いと思います。
具体的には、以下の手順を踏みます。
- emacsを起動する
Alt+x
(M-x)を押す- 下の方に
M-x
と出るので、package-refresh-contents
と入力してEnter続いて以下のコマンドを打ちます
M-x package-install flycheck M-x package-install company-go
Alt+x
(M-x)を押す- 下の方に
M-x
と出るので、package-install
と入力してEnterInstall package:
と出るので、flycheck
と入力してEntercompany-go
についても同様最後に
先ほどinit.elを作成した場所と同じところに
golang-conf.el
という名前のファイルを作成します。
内容は、参考に書いてあるサイトを参照してください。お疲れ様でした!
上手く設定できていれば、Goのファイルを開くとシンタックスハイライトがついているはずです。
- 投稿日:2020-09-21T01:20:01+09:00
Goで実装する並列処理(Hello, world!を10000回出力してみる)
"Hello, world!"
が100行書き込まれたwrite1.txt
~write100.txt
という名前の100個のテキストファイルを作成する場合、並列の有無で実行時間が変わるのか調べました。Goのバージョン: go1.15.2 windows/amd64
PCの環境: Windows 10 Pro / Intel(R) Core(TM) i5-6300U CPU @2.40GHz (コア数:2, プロセッサ数:4)
プログラムはテキストエディタAtom上で動かしており、下記のコードからは時間計測用の部分を除いています。下記の実行時間はコンパイルの時間を含みません。・並列せずに実行する
package main import ("os"; "strconv") func writeByres(num int) error { filename := "write" + strconv.Itoa(num) + ".txt" file, err := os.Create(filename) if err != nil { return err } defer file.Close() b := []byte("Hello, world!\n") for i := 0; i < 100; i++ { _, err2 := file.Write(b) if err2 != nil { return err2 } } return nil } func main() { i := 1 for i <= 100 { writeByres(i) i++ } }100回の平均実行時間は 0.50095418 秒でした。
・Goroutineで並列する
4並列に制限した場合の例package main import ("fmt"; "sync"; "os"; "strconv"; "io/ioutil") func writeByres(num int, wg *sync.WaitGroup) error { filename := "write" + strconv.Itoa(num) + ".txt" file, err := os.Create(filename) if err != nil { return err } defer file.Close() b := []byte("Hello, world!\n") for i := 0; i < 100; i++ { _, err2 := file.Write(b) if err2 != nil { return err2 } } return nil } func main() { concurrency := 4 limit := make(chan int, concurrency) var wg sync.WaitGroup i := 1 for i <= 100 { wg.Add(1) go func(i int, wg *sync.WaitGroup, limit chan int) { defer wg.Done() limit <- 1 writeByres(i,wg) fmt.Fprintln(ioutil.Discard, "(%d <-limit)\n", <-limit) // 出力を破棄 // fmt.Printf("No. %d done. (%d <-limit)\n", i, <-limit) }(i, &wg, limit) i++ } wg.Wait() }このスクリプトでは
concurrency
の値で並列数の上限を規定しています。これを変えたときの結果を以下に示します。
concurrency(並列数) 100回の平均実行時間 1 0.61596947 秒 2 0.42971765 秒 4 0.25714048 秒 8 0.24074216 秒 16 0.27779638 秒 32 0.28692588 秒 64 0.35032400 秒 並列せずに実行すると約 0.5秒 を要していましたが、4並列の場合はその半分の時間で済むという結果になりました。並列処理の方が明らかに短時間で済んでいます。
また、4並列以上では実行時間の改善が見られず、頭打ちになっている(寧ろ時間が掛かってしまう)ことも観察できました。これはコードの問題というよりはマシン側の制約による所が大きいと考えられます。
Go言語は並列処理が得意だというのは聞いていましたが、実際に簡単な例で並列処理のメリットを体感することができました。ただし、やたらと並列すれば作業が効率化できるかと言われるとそうではなく、使用できるコア数やスレッド数、処理すべき作業の量などを踏まえた上で能率の最大化を目指すべきですね。
・参考資料
「Golang ゴールーチンで並列数を指定して実行」
「go での非同期処理 その2」
「Go言語初心者がGo言語をさわってみた」
- 投稿日:2020-09-21T00:02:43+09:00
Goの学習備忘録 #2 goenvでバージョン管理しようとしたら古いバージョンしか見つかりません
概要
Go言語を扱う際に、goenvを導入しようとしたら、古いバージョンしか見つからず詰まった話
homebrewでgoenvをダウンロード
macだしhomebrewでいいかなと思い、
$ brew install goenv
無事にインストールは完了インストールできるgolangのバージョンを確認
$ goenv install -l => Available versions: 1.2.2 1.3.0 1.3.1 : : 1.11.0 1.11beta2 1.11beta3 1.11rc1 1.11rc2 1.11.1 1.11.2 1.11.3 1.11.4 1.12beta1あれ?1.15.2がstable versionになってたから入れたいのにリストにない
あーgoenvのバージョン古いからか$ brew upgrade goenv => Warning: goenv 1.23.3 is already installedん?どういうこと?
goenvでバージョン管理
homebrewじゃなくてgitでインストール
1.23系から2.0系にする場合は、git cloneで入れればよいという記事がちらほらあったので、gitでインストールする
公式に倣いインストール
$ git clone https://github.com/syndbg/goenv.git ~/.goenv
PATHを通す
export GOENV_ROOT=$HOME/.goenv export PATH=$GOENV_ROOT/bin:$PATH eval "$(goenv init -)" export PATH="$GOROOT/bin:$PATH" export PATH="$PATH:$GOPATH/bin"PATHを通したところで、コードを実行
$ go run main.go main.go:1499:2: package myProject/mylib is not in GOROOT (/myProject/mylib) main.go:1500:2: package myProject/mylib/hogehoge is not in GOROOT (myProject/mylib/hogehoge) $ go build go: cannot find main module; see 'go help modules'runもbuildもうまく動かない
gomod
Goのコードはライブラリも含めてすべて\$GOPATH/src以下に置くという約束になっている。
将来の Go 1.12 からこのやり方を改め、\$GOPATH/srcやvendorは廃止となりGo Modulesという物を採用する事になった。今回は1.15.2を使用しているので、gomodを作成する必要があるみたい
golangの設定変更
go env -w GO111MODULE=ongomod作成
プロジェクトの直下で
$ go mod init myProject $ go build $ go run main.go => Human!!無事動いた