Go 言語の文字列の基本

GolangGolangBeginner
今すぐ練習

💡 このチュートリアルは英語版からAIによって翻訳されています。原文を確認するには、 ここをクリックしてください

はじめに

前のレッスンでは、Goの文字はUTF-8でエンコードされ、byte型またはrune型として格納されることを学びました。次に、文字のコレクションである文字列について説明しましょう。一緒にこのトピックを探ってみましょう。

ポイント

  • 文字列とは
  • 文字列の作成
  • 文字列の宣言
  • 一般的な文字列関数
  • 文字列要素のアクセス

Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL go(("Golang")) -.-> go/BasicsGroup(["Basics"]) go(("Golang")) -.-> go/DataTypesandStructuresGroup(["Data Types and Structures"]) go(("Golang")) -.-> go/FunctionsandControlFlowGroup(["Functions and Control Flow"]) go(("Golang")) -.-> go/ObjectOrientedProgrammingGroup(["Object-Oriented Programming"]) go(("Golang")) -.-> go/AdvancedTopicsGroup(["Advanced Topics"]) go/BasicsGroup -.-> go/values("Values") go/BasicsGroup -.-> go/variables("Variables") go/DataTypesandStructuresGroup -.-> go/strings("Strings") go/FunctionsandControlFlowGroup -.-> go/for("For") go/FunctionsandControlFlowGroup -.-> go/functions("Functions") go/ObjectOrientedProgrammingGroup -.-> go/struct_embedding("Struct Embedding") go/AdvancedTopicsGroup -.-> go/text_templates("Text Templates") go/AdvancedTopicsGroup -.-> go/number_parsing("Number Parsing") subgraph Lab Skills go/values -.-> lab-149069{{"Go 言語の文字列の基本"}} go/variables -.-> lab-149069{{"Go 言語の文字列の基本"}} go/strings -.-> lab-149069{{"Go 言語の文字列の基本"}} go/for -.-> lab-149069{{"Go 言語の文字列の基本"}} go/functions -.-> lab-149069{{"Go 言語の文字列の基本"}} go/struct_embedding -.-> lab-149069{{"Go 言語の文字列の基本"}} go/text_templates -.-> lab-149069{{"Go 言語の文字列の基本"}} go/number_parsing -.-> lab-149069{{"Go 言語の文字列の基本"}} end

文字列とは

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つの空の文字列と文字列labexabcを宣言しました。単一バイト文字のみを含んでいるため、それらのバイト長と実際の長さが同じことがわかります。

文字列要素のアクセス

文字列は基本的にバイトのシーケンスであるため、配列と同じように、文字列内の個々のバイトまたは文字にインデックスを使ってアクセスできます。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

この出力では、次のことがわかります。

  1. 特定のインデックスで文字列をアクセスすると、byteが返されます。
  2. for...rangeループを使うと、正しくruneを反復処理できます。

この例は、byteruneの主な違いと、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()が正しく変換すると、errnilを返します。変換中にエラーが発生すると、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()は、文字列の先頭と末尾の空白のみを削除することに注意してください。文字列内の空白はそのまま残ります。

まとめ

このレッスンで学んだことをまとめると、以下の通りです。

  • 文字列と文字の関係
  • 文字列を宣言する2つの方法、二重引用符と逆引用符を使う方法
  • インデックスを使った文字列要素のアクセス(バイトアクセス)とfor...rangeを使ったアクセス(runeアクセス)
  • len()を使った文字列の長さの取得(バイト長)とutf8.RuneCountInStringを使った取得(文字/rune長)
  • strconv.Atoi()strconv.Itoa()を使った文字列と整数の変換
  • +演算子とfmt.Sprintf()を使った文字列の連結
  • strings.TrimSpace()を使った文字列の先頭と末尾の空白の削除

このレッスンでは、日常生活で使う文字列を説明しました。文字列と文字の関係を学び、文字列の作成と宣言を習得し、一般的な文字列関数についても知識を得ました。また、文字列内の個々の文字を安全にアクセスする方法、特に多バイト文字を扱う際の方法を学び、重要な文字列操作方法を理解するようになりました。これにより、Goで文字列データを扱うための強固な基礎が築けました。