Skip to content

Latest commit

 

History

History
239 lines (173 loc) · 7.82 KB

4.04.md

File metadata and controls

239 lines (173 loc) · 7.82 KB

セクション 4.4: 文字列型

String Typeを見てみましょう。Stringは複雑なものになりがちですが、_簡単な部分_を見ていきましょう。Goが目指しているのものを覚えているでしょうか。効率的なコンパイル、効率的な実行、プログラミングを簡単にすること、です。

文字列型の変数を作ってみましょう。文字列はダブルクォート"やバッククォート"`"を用いて作ることができます。バッククォートで作られる文字列はraw string literalです。なので、改行やスペースなどが全て含まれます。

package main

import (
	"fmt"
)

var string_literal string

func main() {
	s := "Hello, playground"
	string_literal = `"Hello,
	
	playground"`
	fmt.Println(s)
	fmt.Printf("%T\n", s)
	fmt.Println(string_literal)
	fmt.Printf("%T\n", string_literal)
}

playground

Go Specでは文字列型について以下のようにしています。

文字列型は文字列の値の集合です。文字列の値とは(空文字を含む)データ(byte)が順番に並んでいるものです。文字列は変更することができません。一度作られれば、その文字列の内容を変更することは不可能です。定義済みのための文字列のための型はstringです。

そう、_データ(byte)が順番に並んでいるもの_です。byteは型で、int8のエイリアスです。文字列型をbyte型の並びに_型変換_してみましょう。bs := []byte(s)

package main

import (
	"fmt"
)

var string_literal string

func main() {
	s := "Hello, playground"
	fmt.Println(s)
	fmt.Printf("%T\n", s)
	bs := []byte(s)
	fmt.Println(bs)
	fmt.Printf("%T\n", bs)
}

playground

こんな出力結果が出たでしょうか。[72 101 108 108 111 44 32 112 108 97 121 103 114 111 117 110 100]

ASCIIコーディングスキーマを見てみると、72"H"101, 108, 108, 111はそれぞれ"e", "l", "l", "o"となっています。これは先程のコード中の文字列を表しています。アルファベットの文字を表現するためにコーディングスキーマを使っています。ここでは、10進数の数字で文字の並びを表しています。プログラミングでは16進数もよく使います。

UTF-8では、1から4バイトでコードポイントを作り、それぞれのコードポイントがそれぞれ1つの文字を表しています。
72"H"と関連付けられているのを見ましたが、ASCIIコーディングスキーマでは72は2進数の100 1000と結びついています。これは16進数でいう48です。

16進数、UTF-8についてもう少し詳しく見ていきましょう。fmt package documentation%x周辺はこうなっています。

%s	処理されていない文字列またはスライス
%q	ダブルクォートで囲まれたGoの文法的に安全にエスケープされた文字列
%x	1バイトにつき2つも文字の小文字の16進数
%X	1バイトにつき2つも文字の大文字の16進数

もう少し下の"Other flags:"セクションは以下のようになっています。

+	数値型の記号を常に出力する
	%q (%+q)の際にASCII文字のみの出力になるようにする
-	スペースで左詰めする
#	代替フォーマット: 8進数の場合に0を追加 (%#o), 16進数の場合に0xを追加 (%#x);
	16進数のために0Xを追加 (%#X); %pのために0xを抑制 (%#p);
	%qために、strconv.CanBackquoteがtrueの場合にバッククォートで囲まれた生文字列を出力
	%e, %E, %f, %F, %g, %Gのために10進数ポイントを常に出力
	%gと%Gの際に末尾の0を削除しない
	%U (%#U)の際に文字列が出力可能なら U+00078 'x'のように出力する
' '	(スペース) スペースを残す (% d);
	出力される文字列や16進数ごとにスペースを追加する。(% x, % X)
0	0埋めする
    数値の際には記号の後に0を移動する

%U (%#U)の際に文字列が出力可能なら U+00078 'x'のようにするの部分にならってUTF-8の文字とともに%#Uを使ってみましょう。

package main

import (
	"fmt"
)

var string_literal string

func main() {
	s := "Hello, playground"
	fmt.Println(s)
	fmt.Printf("%T\n", s)
	
	bs := []byte(s)
	fmt.Println(bs)
	fmt.Printf("%T\n", bs)
	
	for i := 0; i < len(s); i++ {
		fmt.Printf("%#U ", s[i])
	}
}

playground

sの内容のindexや16進数での値を出力してみます。

package main

import (
	"fmt"
)

var string_literal string

func main() {
	s := "Hello, playground"
	fmt.Println(s)
	fmt.Printf("%T\n", s)

	bs := []byte(s)

	/* 文字列sの文字をそれぞれ10進数で */
	fmt.Println(bs)
	fmt.Printf("%T\n", bs)

	/* 文字列sの文字をそれぞれUTF-8のコードポイントで */
	for i := 0; i < len(s); i++ {
		fmt.Printf("%#U ", s[i])
	}

	fmt.Println("")

	/* 文字列sのそれぞれの文字を16進数で */
	for i, v := range s {
		fmt.Printf("At index position %d we have hex %#x\n", i, v)
	}
}

playground

ASCIIのコード表を使って出力が正しいか見てみましょう。

Binary Hex Glyph
100 1000 48 H
110 0101 65 e
110 1100 6C l
110 1100 6C l
110 1111 6F o
010 1100 2C ,
010 0000 20 space
111 0000 70 p
110 1100 6C l
110 0001 61 a
111 1001 79 y
110 0111 67 g
111 0010 72 r
110 1111 6F o
111 0101 75 u
110 1110 6E n
110 0100 64 d

"Hello, playground"のそれぞれの文字は以下のようになります。

10進数の場合

[72 101 108 108 111 44 32 112 108 97 121 103 114 111 117 110 100]

UTF-8コードポイントの場合

U+0048 'H' U+0065 'e' U+006C 'l' U+006C 'l' U+006F 'o' U+002C ',' U+0020 ' ' U+0070 'p' U+006C 'l' U+0061 'a' U+0079 'y' U+0067 'g' U+0072 'r' U+006F 'o' U+0075 'u' U+006E 'n' U+0064 'd'

16進数の場合

0x48 0x65 0x6c 0x6c 0x6f 0x2c 0x20 0x70 0x6c 0x61 0x79 0x67 0x72 0x6f 0x75 0x6e 0x64

それぞれのコードポイントはint32のエイリアスであるruneとしても表せます。runeのコードポイントはUTF-8です。

より深く

おさらいです。文字列はダブルクォートやバッククォートによって囲むことで作ることができます。UTF-8でGoのコードを書いても文字列がUTF-8コードポイントになるわけではありません。

文字列はデータの並びです。文字列は変更することはできませんが、新しい値を割り当てることはできます。

package main

import (
	"fmt"
)

var string_literal string

func main() {
	s := "Hello, playground"
	s = "Hello, Hawaii"
	fmt.Println(s)
}

playground

このように、stringのデータを変えることはできなくても、新しい値を割り当てることはできます。stringを構成するデータは変換したり、覗くこともできます。ASCIIとUTF-8コーディングスキーマとの変換もできました。

Golangのウェブサイトでは、アジア圏の文字を例に使っています。このセクションで覚えたことを使ってこれらの文字は_1バイト以上であること_を確認してみてください。

package main

import "fmt"

func main() {
	fmt.Println("Hello, 世界")
}