Slide 1

Slide 1 text

runeとUnicodeと文字数と 文字ってなんだよ......ってなる話 by ことね@_ktnyt @ Go勉強会 by bitkey × voicy

Slide 2

Slide 2 text

自己紹介 ことね(板谷美玲)@_ktnyt LAPRAS株式会社 Webエンジニア io.Readerをすこれの人 趣味 プログラミング、ドライブ、音楽

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

文字数、数えられますか?

Slide 5

Slide 5 text

これらの文字列は何文字でしょう? 1: A 2: あ 3: ㌖ 4: 5: 6: 7:

Slide 6

Slide 6 text

試してみよう! package main import "fmt" func main() { ss := []string{ "A", " あ", " ㌖", " ", " ", " ", " ", } fmt.Println("| s | len(s) |") fmt.Println("|:-:|-------:|") for _, s := range ss { fmt.Printf("| %s | %d |\n", s, len(s)) } }

Slide 7

Slide 7 text

結果 s len(s) A 1 あ 3 ㌖ 3 4 13 17 25

Slide 8

Slide 8 text

なんで? Goにおける len() の仕様 Call Argument type Result len(s) string type string length in bytes [n]T, *[n]T array length (== n) []T slice length map[K]T map length (number of defined keys) chan T number of elements queued in channel buffer type parameter see below cap(s) [n]T, *[n]T array length (== n) []T slice capacity chan T channel buffer capacity type parameter see below from: https://go.dev/ref/spec#Length_and_capacity

Slide 9

Slide 9 text

よろしい、ならばruneだ Rune literals A rune literal represents a rune constant, an integer value identifying a Unicode code point. A rune literal is expressed as one or more characters enclosed in single quotes, as in 'x' or '\n'. Within the quotes, any character may appear except newline and unescaped single quote. A single quoted character represents the Unicode value of the character itself, while multi-character sequences beginning with a backslash encode values in various formats. from: https://go.dev/ref/spec#Rune_literals

Slide 10

Slide 10 text

Unicode Code Point? Any value in the Unicode codespace; that is, the range of integers from 0 to 0x10FFFF. from: https://www.unicode.org/glossary/#code_point

Slide 11

Slide 11 text

与太話 UnicodeとUTF-8/UTF-16 Unicodeの文字空間である 0x0-0x10FFFF をエンコードするのに必要なのは 21 bit で、8N bit (N byte) のサイズを持つ変数で表現するのには最低 24 bit、実装上殆どの場合 32 bit が用 いられる。UTF-8 は Unicode の Code Point を 8 bit 単位(実際にはプレフィックスがつく ので厳密には 8bit ではないが)、UTF-16 は 16 bit 単位(同上)の Code Unit で保持する。

Slide 12

Slide 12 text

runeで数えてみよう package main import "fmt" func main() { ss := []string{ "A", " あ", " ㌖", " ", " ", " ", " ", } fmt.Println("| s | len(s) | len([]rune(s)) |") fmt.Println("|:-:|-------:|---------------:|") for _, s := range ss { fmt.Printf("| %s | %d | %d |\n", s, len(s), len([]rune(s))) } }

Slide 13

Slide 13 text

結果 s len(s) len([]rune(s)) A 1 1 あ 3 1 ㌖ 3 1 4 1 13 4 17 5 25 7

Slide 14

Slide 14 text

なんでや

Slide 15

Slide 15 text

Code Point 表記 package main import ( "fmt" "strings" ) func main() { ss := []string{ "A", " あ", " ㌖", " ", " ", " ", " ", } fmt.Println("| s | Code Points |") fmt.Println("|:-:|:------------|") for _, s := range ss { rr := []rune(s) cp := make([]string, len(rr)) for i, r := range rr { cp[i] = fmt.Sprintf("%U", r) } fmt.Printf("| %s | %s |\n", s, strings.Join(cp, " ")) } }

Slide 16

Slide 16 text

s Code Points A U+0041 あ U+3042 ㌖ U+3316 U+1F64F U+1F64B U+200D U+2640 U+FE0F U+1F64B U+1F3FB U+200D U+2640 U+FE0F U+1F469 U+200D U+1F469 U+200D U+1F467 U+200D U+1F467 U+200D: Zero Width Space, U+FE0F: Variant Selector

Slide 17

Slide 17 text

UAX #29: UNICODE TEXT SEGMENTATION https://unicode.org/reports/tr29/ Unicodeの文字区切りについての仕様。 たとえばハングルでは個別に音を表現するパーツを組み合わせて一つの文字を作るので、 Unicode的に複数の Code Point でも一つに見せる必要がある。 一文字に見える複数の Code Point からなる文字列を Grapheme Cluster と呼ぶ。

Slide 18

Slide 18 text

つまり? Code Unit→ 8 bit (UTF-8), 16 bit (UTF-16), etc. UTF-8 は ascii を効率よくエンコーディングできる。 Unicodeの一文字 (Code Point) → 最低 21 bit (multi-byte) UTF-8 → 1-4 byte Go rune → 32 bit 見かけ上の一文字 (Grapheme Cluster) → 複数の Code Point

Slide 19

Slide 19 text

rivo/uniseg package main import ( "fmt" "github.com/rivo/uniseg" ) func main() { ss := []string{ "A", " あ", " ㌖", " ", " ", " ", " ", } fmt.Println("| s | len(s) | len([]rune(s)) | uniseg.GraphemeClusterCount |") fmt.Println("|:-:|-------:|---------------:|----------------------------:|") for _, s := range ss { fmt.Printf("| %s | %d | %d | %d |\n", s, len(s), len([]rune(s)), uniseg.GraphemeClusterCount(s)) } }

Slide 20

Slide 20 text

結果 s len(s) len([]rune(s)) uniseg.GraphemeClusterCount A 1 1 1 あ 3 1 1 ㌖ 3 1 1 4 1 1 13 4 1 17 5 1 25 7 1

Slide 21

Slide 21 text

「午前3時のいばらきけん」に様々な Combining Diacritical Mark をつけたテキスト。 Unicode的には11文字+いろんな修飾という認識になる。 len(s) len([]rune(s)) uniseg.GraphemeClusterCount 129 60 11

Slide 22

Slide 22 text

「文字」って難しい 「㌖」は日本人的には6文字だけどUnicode的には一文字だったり。 「 」は人間的には一文字だけどUnicode的には7文字だったり。 「文字」って簡単そうで実は難しい。