post Image
Go で UTF-8 の文字列を扱う

Go で UTF-8 の文字列の扱いに慣れるために練習で書いたコードをまとめました。

文字列を表示する

package main

func main() {
    str := "あいうえお"
    buf := []byte("あいうえお")
    runes := []rune("あいうえお")

    println(str)
    println(string(buf))
    println(string(runes))
}

文字列リテラルを使う

package main

func main() {
    str := "か\u3099\u3099\u3099\u3099\u3099"
    str2 := "DOG FACE \U0001F436"

    println(str)
    println(str2)
}

文字列リテラルを使う場合、次のとおり。

package main

func main() {
    str := "\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88\xe3\x81\x8a"
    println("あいうえお" == str)
}

数値から文字列を生成する

次のようにコードポイントをあらわす数値から rune スライスをつくることができる。

package main

func main() {
    runes := []rune{0x3042, 0x3044, 0x3046, 0x3048, 0x304A}
    println(string(runes))
}

バイト列をあらわす数値から byte スライスを次のようにつくることができる。

package main

func main() {
    buf := []byte{0xe3, 0x81, 0x82, 0xe3, 0x81, 0x84, 0xe3, 0x81, 0x86, 0xe3, 0x81, 0x88, 0xe3, 0x81, 0x8a}
    println(string(buf))
}

Go の仕様

string、byte、rune の相互変換ルール

Go の仕様ドキュメントの Conversions to and from a string type にまとめられています。

符号つきもしくは符号なしの整数値を string 型に変換すると、整数の UTF-8 表現を含む文字列が生成されます。有効な Unicode コードポイントの範囲の外にある値は “\uFFFD” に変換されます。

string('a')       // "a"
string(-1)        // "\ufffd" == "\xef\xbf\xbd"
string(0xf8)      // "\u00f8" == "ø" == "\xc3\xb8"
type MyString string
MyString(0x65e5)  // "\u65e5" == "日" == "\xe6\x97\xa5"

byte のスライスを string 型に変換すると、スライスの要素が連続したバイト列である文字列が生成されます。

string([]byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'})   // "hellø"
string([]byte{})                                     // ""
string([]byte(nil))                                  // ""

type MyBytes []byte
string(MyBytes{'h', 'e', 'l', 'l', '\xc3', '\xb8'})  // "hellø"

rune のスライスを string 型に変換すると、それぞれの rune の値を文字に変換して連結した文字列が生成されます。

string([]rune{0x767d, 0x9d6c, 0x7fd4})   // "\u767d\u9d6c\u7fd4" == "白鵬翔"
string([]rune{})                         // ""
string([]rune(nil))                      // ""

type MyRunes []rune
string(MyRunes{0x767d, 0x9d6c, 0x7fd4})  // "\u767d\u9d6c\u7fd4" == "白鵬翔"

string 型の値を byte のスライスに変換すると、連続した要素が文字列のバイトであるスライスが生成されます。

[]byte("hellø")   // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
[]byte("")        // []byte{}

MyBytes("hellø")  // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}

string 型の値を rune のスライスに変換すると、それぞれの文字の Unicode コードポイントを保有するスライスが生成されます。

[]rune(MyString("白鵬翔"))  // []rune{0x767d, 0x9d6c, 0x7fd4}
[]rune("")                 // []rune{}

MyRunes("白鵬翔")           // []rune{0x767d, 0x9d6c, 0x7fd4}

バイト数を求める

package main

func main() {
    str := "あいうえお"
    buf := []byte("あいうえお")
    runes := []rune("あいうえお")

    println(15 == len(str))
    println(15 == len(buf))
    println(15 == len(string(runes))) 
}

文字数を求める

package main

import "unicode/utf8"

func main() {
    str := "あいうえお"
    buf := []byte("あいうえお")
    runes := []rune("あいうえお")

    println(5 == utf8.RuneCountInString(str))
    println(5 == utf8.RuneCount(buf))
    println(5 == len(runes))
}

1文字ずつアクセスする

package main

func main() {
    str := "あいうえお"

    for index, runeValue := range str {
        println("位置:", index, "文字:", string([]rune{runeValue}))
    }
}

rune 型のスライスの場合、次のようになる。

package main

import (
    "fmt"
)

func main() {
    runes := []rune("あいうえお")

    for i := range runes {
        fmt.Printf("%c\n", runes[i])
    }
}

utf8.DecodeRuneInString を使う場合、次のように書ける。

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    str := "あいうえお"

    for len(str) > 0 {
        r, size := utf8.DecodeRuneInString(str)
        fmt.Printf("%c\n", r)

        str = str[size:]
    }
}

不正なバイト列

不正なバイト列が含まれるかチェックする

package main

import "unicode/utf8"

func main() {
    str := "あいうえお\x80"
    buf := []byte("あいうえお\x80")

    println(false == utf8.ValidString(str))
    println(false == utf8.Valid(buf))
}

不正なバイト列を代替文字の U+FFFD に置き換える

package main

func main() {
    str := "あいうえお\x80"
    buf := []byte("")

    for _, runeValue := range str {
        buf = append(buf, string(runeValue)...)
    }

    println(string(buf))
}

東アジアの文字幅

文字幅の種類を調べる

文字幅の種類をあらわす定数として EastAsianAmbiguousEastAsianWideEastAsianNarrowEastAsianFullwidthEastAsianHalfwidth が定義される。

package main

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

func main() {
    s := "あいうえお"
    p, _ := width.LookupString(s)

    println(width.EastAsianWide == p.Kind())
}

全角文字と半角文字の相互変換

package main

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

func main() {
    s := "ハンカクカタカナ"
    w := width.Widen.String(s)
    fmt.Printf("%U: %s\n", []rune(s), s)
    fmt.Printf("%U: %s\n", []rune(w), w)
}
package main

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

func main() {
    s := "ゼンカクカタカナ"
    w := width.Narrow.String(s)
    fmt.Printf("%U: %s\n", []rune(s), s)
    fmt.Printf("%U: %s\n", []rune(w), w)
}

Unicode 照合

package main

import (
    "golang.org/x/text/collate"
    "golang.org/x/text/language"
)

func main() {
    c := collate.New(language.Japanese)

    println(0 == c.CompareString("a", "a"))
    println(-1 == c.CompareString("a", "A"))
    println(1 == c.CompareString("A", "a"))

    println(0 == c.CompareString("が", "か\u3099"))
}

Unicode 正規化

package main

import "golang.org/x/text/unicode/norm"

func main() {
    buf := norm.NFD.Bytes([]byte("がぎぐげご"))
    str := norm.NFD.String("がぎぐげご")
    expected := "か\u3099\u3099\u3099\u3099\u3099"

    println(expected == string(buf))
    println(expected == str)
}

U+FDFA に NFKD を適用すると18文字になる。Unicode 正規化を適用して長くなる文字の例は 「What are the maximum expansion factors for the different normalization forms?」(Unicode.org)の記事を参照。

package main

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

func main() {
    str := norm.NFKD.String("\uFDFA")

    println(18 == utf8.RuneCountInString(str))
}

基底文字の後に続く装飾記号の個数が31以上である場合、正規化は適用されない。

package main

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

func main() {
    str := "か" + strings.Repeat("\u3099", 30)
    ret := norm.NFC.String(str)

    println(30 == utf8.RuneCountInString(ret))

    str2 := "か" + strings.Repeat("\u3099", 32)
    ret2 := norm.NFC.String(str2)

    println(33 == utf8.RuneCountInString(ret2))
}

『 Go 』Article List
Category List

Eye Catch Image
Read More

Androidに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

AWSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Bitcoinに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

CentOSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

dockerに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

GitHubに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Goに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Javaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

JavaScriptに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Laravelに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Pythonに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Rubyに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Scalaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Swiftに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Unityに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Vue.jsに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Wordpressに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

機械学習に関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。