はじめに
前のレッスンでは、Goの文字はUTF-8でエンコードされ、byte
型またはrune
型として格納されることを学びました。次に、文字のコレクションである文字列について説明しましょう。一緒にこのトピックを探ってみましょう。
ポイント:
- 文字列とは
- 文字列の作成
- 文字列の宣言
- 一般的な文字列関数
- 文字列要素のアクセス
前のレッスンでは、Goの文字はUTF-8でエンコードされ、byte
型またはrune
型として格納されることを学びました。次に、文字のコレクションである文字列について説明しましょう。一緒にこのトピックを探ってみましょう。
ポイント:
Goで学んだ最初のプログラムでは、文字列"hello, world"
を出力しました。
文字列はGoの基本データ型であり、文字列リテラルとも呼ばれます。文字のコレクションとして理解でき、連続したメモリブロックを占有します。このメモリブロックには、文字、文章、絵文字など、任意の種類のデータを格納できます。
ただし、他の言語とは異なり、Goの文字列は不変であり、変更することができません。これは、文字列が作成されると、個々の文字を変更できないことを意味します。文字列を変更したい場合は、新しい文字列を作成する必要があります。
文字列はいくつかの方法で宣言できます。最初の方法を見てみましょう。string.go
という名前の新しいファイルを作成します。
touch ~/project/string.go
次のコードを書きます。
package main
import "fmt"
func main() {
// varキーワードを使用して文字列変数aを作成する
var a string = "labex"
a = "labex" // 変数aに"labex"を代入する
// 変数bを宣言して値を代入する
var b string = "labs"
// 型宣言を省略できる
var c = "Monday"
// :=を使用して迅速な宣言と代入を行う
d := "Sunday"
fmt.Println(a, b, c, d)
}
上記のコードは、var
キーワードと:=
演算子を使用して文字列を作成する方法を示しています。var
を使用して変数を作成する際に値を代入する場合は、型宣言を省略できます。これは、変数c
の作成時に示されています。:=
演算子は、Goで変数を宣言および初期化するための省略記法です。これにより、割り当てられた値から変数の型が自動的に推論されます。これを使用することで、コードをより簡潔にすることができます。
go run string.go
期待される出力は次のとおりです。
labex labs Monday Sunday
ほとんどの場合、文字列を宣言する際には二重引用符""
を使用します。二重引用符の利点は、エスケープシーケンスとして使用できることです。たとえば、以下のプログラムでは、\n
エスケープシーケンスを使用して新しい行を作成しています。
package main
import "fmt"
func main() {
x := "linux\nlabex"
fmt.Println(x)
}
go run string.go
期待される出力は次のとおりです。
linux
labex
以下は、一般的なエスケープシーケンスです。
シンボル | 説明 |
---|---|
\n |
新しい行 |
\r |
復帰 |
\t |
タブ |
\b |
バックスペース |
\\ |
バックスラッシュ |
\' |
シングルクォート |
\" |
ダブルクォート |
テキストの元の形式を維持したい場合や複数行を使用する必要がある場合は、逆引用符を使用して表現できます。
package main
import "fmt"
func main() {
// "labex"のアスキーアートを出力する
ascii := `
## ##### ######### ########## ## #
## ## ## ## ## ## ## #
## ## ## ######### ######### ##
## ########## ## ## ## ##
## ## ## ## ## ## ## #
########## ## ## ######### ########## ## #`
fmt.Println(ascii)
}
逆引用符は、プロンプト、HTMLテンプレート、出力の元の形式を維持する必要がある他のケースで一般的に使用されます。逆引用符の中のテキストは、生の文字列リテラルとして扱われます。つまり、エスケープシーケンスは解釈されません。これにより、エスケープすることなく複数行のテキストや特殊文字を含めることができます。
前のレッスンでは、英字と一般的な句読点は1バイトを占めることを学びました。
したがって、Goでは、len()
関数を使用して文字列をバイト長で取得できます。複数バイトを占める文字がなければ、len()
関数を使用して文字列の長さを正確に測ることができます。
文字列に複数バイトを占める文字が含まれている場合は、utf8.RuneCountInString
関数を使用して文字列内の実際の文字数を取得できます。
例を見てみましょう。string.go
ファイルに次のコードを書きます。
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
// varと:=を使用して2つの空の文字列を宣言する
var a string
b := ""
c := "labex"
d := "abc"
// バイト長を出力する
fmt.Printf("The value of a is %s, the byte length of a is: %d\n", a, len(a))
fmt.Printf("The value of b is %s, the byte length of b is: %d\n", b, len(b))
fmt.Printf("The value of c is %s, the byte length of c is: %d\n", c, len(c))
fmt.Printf("The value of d is %s, the byte length of d is: %d\n", d, len(d))
// 文字列の長さを出力する
fmt.Printf("The length of c is: %d\n", utf8.RuneCountInString(c))
fmt.Printf("The length of d is: %d\n", utf8.RuneCountInString(d))
}
go run string.go
期待される出力は次のとおりです。
The value of a is, the byte length of a is: 0
The value of b is, the byte length of b is: 0
The value of c is labex, the byte length of c is: 5
The value of d is abc, the byte length of d is: 3
The length of c is: 5
The length of d is: 3
プログラムでは、まず2つの空の文字列と文字列labex
とabc
を宣言しました。単一バイト文字のみを含んでいるため、それらのバイト長と実際の長さが同じことがわかります。
文字列は基本的にバイトのシーケンスであるため、配列と同じように、文字列内の個々のバイトまたは文字にインデックスを使ってアクセスできます。Goでは、文字列のインデックスは0から始まります。
ただし、インデックスを使って文字列要素にアクセスすると、byte
が返されることに注意してください。文字 (rune
) ではありません。文字列に多バイト文字が含まれている場合、インデックスを使う際には注意が必要です。なぜなら、完全な文字が得られない場合があるからです。個々の文字 (rune
) を安全に取得するには、UTF-8エンコーディングを適切に処理するfor...range
ループを使って文字列を反復処理できます。
string.go
ファイルに次のコードを追加しましょう。
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
str := "Hello, world!"
// インデックスでバイトにアクセスする
fmt.Printf("Byte at index 0: %c\n", str[0]) // 出力: H
fmt.Printf("Byte at index 7: %c\n", str[7]) // 出力: w
// for...rangeを使って反復処理し、安全にruneを取得する
fmt.Println("Iterating through runes:")
for index, char := range str {
fmt.Printf("Index: %d, Char: %c\n", index, char)
}
// runeの数を取得する
fmt.Printf("Number of runes (characters) in the string: %d\n", utf8.RuneCountInString(str))
}
次に、プログラムを実行します。
go run string.go
出力は次のとおりです。
Byte at index 0: H
Byte at index 7: w
Iterating through runes:
Index: 0, Char: H
Index: 1, Char: e
Index: 2, Char: l
Index: 3, Char: l
Index: 4, Char: o
Index: 5, Char:,
Index: 6, Char:
Index: 7, Char: w
Index: 8, Char: o
Index: 9, Char: r
Index: 10, Char: l
Index: 11, Char: d
Index: 12, Char:!
Number of runes (characters) in the string: 13
この出力では、次のことがわかります。
byte
が返されます。for...range
ループを使うと、正しくrune
を反復処理できます。この例は、byte
とrune
の主な違いと、Goの文字列の文字を処理する際に適切な方法を使う重要性を強調しています。
文字列と整数の間で変換するには、strconv
パッケージの関数を使用できます。
package main
import (
"fmt"
"strconv"
)
func main() {
// 文字列aと整数bを宣言する
a, b := "233", 223
// Atoiを使って文字列を整数に変換する
c, _ := strconv.Atoi(a)
// それぞれSprintfとItoa関数を使って
// 整数を文字列に変換する
d1 := fmt.Sprintf("%d", b)
d2 := strconv.Itoa(b)
fmt.Printf("The type of a: %T\n", a) // string
fmt.Printf("The type of b: %T\n", b) // int
fmt.Printf("The type of c: %T\n", c) // int
fmt.Printf("The type of d1: %T\n", d1) // string
fmt.Printf("The type of d2: %T\n", d2) // string
}
go run string.go
期待される出力は次のとおりです。
The type of a: string
The type of b: int
The type of c: int
The type of d1: string
The type of d2: string
プログラムでは、fmt
パッケージのSprintf()
関数を使用しています。この関数は次のような形式を持っています。
func Sprintf(format string, a...interface{}) string
format
はエスケープシーケンスを含む文字列で、a
はエスケープシーケンスに値を提供する定数または変数で、...
はa
と同じ型の変数を複数指定できることを意味します。関数の後ろの文字列は、Sprintf
が文字列を返すことを表しています。この関数の使用例を以下に示します。
a = fmt.Sprintf("%d+%d=%d", 1, 2, 3)
fmt.Println(a) // 1+2=3
このコードスニペットでは、format
に3つの整数変数1、2、3が渡されています。format
内の%d
整数エスケープ文字が整数値に置き換えられ、Sprintf
関数は置き換え後の結果1+2=3
を返します。
また、strconv.Atoi()
を使って文字列を整数に変換する際、関数は2つの値を返します。変換された整数val
とエラーコードerr
です。Goでは、変数を宣言すると必ず使用しなければならないため、アンダースコア_
を使ってerr
変数を無視できます。
strconv.Atoi()
が正しく変換すると、err
はnil
を返します。変換中にエラーが発生すると、err
はエラーメッセージを返し、val
の値は0になります。文字列a
の値を変更し、アンダースコアを通常の変数に置き換えて自分で試してみることができます。これは、エラーハンドリングの良い慣例であり、Goプログラミングの重要な部分です。
2つ以上の文字列を連結する最も簡単な方法は、+
演算子を使用することです。また、fmt.Sprintf()
関数を使って文字列を連結することもできます。例を見てみましょう。
package main
import (
"fmt"
)
func main() {
a, b := "lab", "ex"
// 最も簡単な方法、+を使って連結する
c1 := a + b
// Sprintf関数を使って連結する
c2 := fmt.Sprintf("%s%s", a, b)
fmt.Println(a, b, c1, c2) // lab ex labex labex
}
go run string.go
期待される出力は次のとおりです。
lab ex labex labex
プログラムでは、fmt
パッケージのSprintf()
関数を使って文字列を連結し、結果を出力しました。どちらの方法も文字列を連結する一般的な方法であり、それらの選択は多くの場合、読みやすさと個人的な好みに依存します。
文字列を使って先頭と末尾の空白を削除するには、strings.TrimSpace
関数を使用できます。この関数は文字列を入力として受け取り、先頭と末尾の空白が削除された文字列を返します。形式は次のとおりです。
func TrimSpace(s string) string
以下は例です。
package main
import (
"fmt"
"strings"
)
func main() {
a := " \t \n labex \n \t labs"
fmt.Println(strings.TrimSpace(a))
}
go run string.go
期待される出力は次のとおりです。
labex
labs
strings.TrimSpace()
は、文字列の先頭と末尾の空白のみを削除することに注意してください。文字列内の空白はそのまま残ります。
このレッスンで学んだことをまとめると、以下の通りです。
for...range
を使ったアクセス(runeアクセス)len()
を使った文字列の長さの取得(バイト長)とutf8.RuneCountInString
を使った取得(文字/rune長)strconv.Atoi()
とstrconv.Itoa()
を使った文字列と整数の変換+
演算子とfmt.Sprintf()
を使った文字列の連結strings.TrimSpace()
を使った文字列の先頭と末尾の空白の削除このレッスンでは、日常生活で使う文字列を説明しました。文字列と文字の関係を学び、文字列の作成と宣言を習得し、一般的な文字列関数についても知識を得ました。また、文字列内の個々の文字を安全にアクセスする方法、特に多バイト文字を扱う際の方法を学び、重要な文字列操作方法を理解するようになりました。これにより、Goで文字列データを扱うための強固な基礎が築けました。