Go 言語における文字型

GolangBeginner
オンラインで実践に進む

はじめに

前回のセクションでは、よく使われる数値型について解説しました。このセクションでは、Go 言語における文字型について学んでいきましょう。

学習ポイント:

  • ASCII エンコーディング
  • UTF-8 エンコーディング
  • Unicode 文字セット
  • byte
  • rune

ASCII エンコーディング

コンピュータの黎明期には、ASCII(American Standard Code for Information Interchange)というエンコーディング形式が使われていました。これは 7 ビットを使用して文字を表現し、128(2^7)種類の文字を扱うことができました。0 から 31 ビットおよび 127 ビットは表示できない制御文字を表し、32 から 126 ビットは日常的に使われる英大文字・小文字、数字、記号を表しています。詳細はこちらの表を参照してください。

コンピュータが普及するにつれ、さまざまな言語をサポートする必要が出てきました。しかし、ASCII エンコーディングでは不十分でした。その結果、簡体字中国語の GB2312、韓国語の EUC-KR、ロシア語の KOI8-R など、言語ごとに独自のエンコーディング形式が開発されました。

しかし、世界中に多くの言語が存在するため、すべての言語を統一できる単一のエンコーディング形式が必要となりました。そのニーズを満たすために誕生したのが Unicode です。

Unicode 文字セット

1991 年、Unicode コンソーシアムは Unicode 文字セットの最初のバージョンをリリースしました。その目的は、すべての言語を単一のエンコーディング形式に統合し、世界中のコンピュータがテキストをより簡単に表示・処理できるようにし、多言語環境での互換性の問題を回避することでした。

ただし、Unicode はあくまで「文字セット」であり、文字に対応するコードを定義しただけで、それらをどのようにメモリやディスクに保存するか(符号化方式)までは規定していませんでした。そのため、インターネットが普及するまでは、広く採用されるには至りませんでした。

UTF-8 エンコーディング

インターネットの継続的な発展に伴い、Unicode の実装エンコーディングである UTF-8 が普及しました。これは可変長エンコーディングであり、文字によってバイト長が異なるのが特徴です。

例えば、ASCII の範囲内にある英文字は 1 バイトで表現されます。文字 'y'(Unicode 値 121)は 1 バイトを占有します。

日常的に使われるほとんどの漢字は 3 バイトを占有します。例えば、「実」(Unicode 値 23454)という文字は 3 バイトです。

ただし、一部の漢字には 4 バイトを占有するものもあります。これは漢字が 10 万文字以上存在するのに対し、3 バイトでは約 6 万文字強しか表現できないため、少数の漢字には 4 バイトが必要になるからです。

UTF-8 エンコーディングのもう一つの利点は、ASCII エンコーディングと後方互換性があることです。実際、ASCII は UTF-8 のサブセットです。UTF-8 の最初の 128 文字は ASCII 文字と一対一で対応しています。つまり、もともと ASCII を使用していたソフトウェアは、ほとんど修正なしでそのまま使い続けることができます。これらの利点により、UTF-8 は徐々に推奨されるエンコーディング形式となりました。

Go 言語の生みの親である Rob Pike 氏と Ken Thompson 氏は、UTF-8 の考案者でもあります。そのため、Go は UTF-8 と非常に親和性が高い言語です。Go ではソースコードファイルを UTF-8 で保存することが義務付けられています。テキスト文字を操作する場合、UTF-8 エンコーディングが最適な選択肢となります。さらに、標準ライブラリには UTF-8 のエンコードとデコードに関する多くの関数が用意されています。

byte と rune

byteuint8 の別名(エイリアス)であり、1 バイト(8 ビット)を占有します。ASCII テーブルにあるすべての文字を表現するのに適しています。しかし、byte が表現できる値の範囲は限られている(256 通り、つまり 2^8)ため、漢字のような複合的な文字を扱うには rune 型を使用する必要があります。

byte.go という新しいファイルを作成し、以下のコードを入力してください。

cd ~/project
touch byte.go
package main

import "fmt"

func main() {
    var a byte = 76
    fmt.Printf("Value of a: %c\n", a)

    var b uint8 = 76
    fmt.Printf("Value of b: %c\n", b)

    var c byte = 'L'
    fmt.Printf("Value of c: %c\n", c)
}

プログラムを実行すると、以下の結果が出力されます。

go run byte.go
Value of a: L
Value of b: L
Value of c: L

%c プレースホルダーは文字を出力するために使用されます。byte 型と uint8 型は、値が同じであれば同じ出力を生成することがわかります。ASCII テーブルを参照すると、文字 'A' の ASCII 値は 65 であることがわかります。整数プレースホルダー %d を使用して出力した場合も、値は 65 になります。

したがって、Go における byte は整数型の uint8 と等価であることが明らかです。同様のことが rune にも言えますが、こちらは異なる範囲の整数値を表します。

runeint32 の別名であり、4 バイト(32 ビット)を占有します。これは、絵文字などの複合的な文字を表現するために使用されます。

byte.go ファイルを以下のコードで更新してください。

package main

import "fmt"

func main() {
    var a rune = '😊' // 笑顔の絵文字
    fmt.Printf("Value of a: %c\n", a)

    var b int32 = 9829 // Unicode 文字の 10 進数表現(ハート記号)
    fmt.Printf("Value of b: %c\n", b)
    var c rune = 0x1F496 // Unicode 文字の 16 進数表現(輝くハートの絵文字)
    fmt.Printf("Value of c: %c\n", c)

    var d rune = '\u0041' // コードポイントによる Unicode 文字表現(大文字の 'A')
    fmt.Printf("Value of d: %c\n", d)
    var e rune = '\U0001F609' // コードポイントによる Unicode 文字表現(ウィンク顔の絵文字)
    fmt.Printf("Value of e: %c\n", e)
}

プログラムを実行すると、以下の出力が表示されます。

go run byte.go

注意: プログラムはデスクトップまたは WebIDE のターミナルで実行してください。LabEx VM の上部にある「Terminal」タブでの実行は避けてください。

Value of a: 😊
Value of b: ♥
Value of c: 💖
Value of d: A
Value of e: 😉
  • 変数 a は笑顔の絵文字 '😊' を表します。
  • 変数 b は Unicode 文字の 10 進数表現(9829)で初期化されており、これはハート記号 '♥' に対応します。
  • 変数 c は Unicode 文字の 16 進数表現(0x1F496)で初期化されており、これは輝くハートの絵文字 '💖' に対応します。
  • 変数 d\u0041 という Unicode 形式を使用して大文字の 'A' を表します。
  • 変数 e\U 形式とコードポイント 0001F609 を使用してウィンク顔の絵文字 '😉' を表します。

注意: Go 言語では、シングルクォート(' ')とダブルクォート(" ")は意味が異なります。シングルクォートは「文字」を表すために使用され、ダブルクォートは「文字列(string)」を宣言するために使用されます。したがって、byte 型や rune 型を宣言する際には必ずシングルクォートを使用してください。そうしないとエラーが発生します。

クイズ

学んだことを復習しましょう。rune.go という新しいファイルを作成し、以下のコードを入力してください。16 進数 0x1F648 を変数 a に代入し、プログラムがその値を正しく出力するようにコードを完成させてください。

  • 要件: rune.go ファイルは ~/project ディレクトリに配置してください。
  • ヒント: 長い 16 進数のコードポイントを文字リテラルとして記述する場合、特定のフォーマットを使用する必要があります。
package main

import "fmt"

func main() {
    var a rune = 0x1F648
    fmt.Printf("The value of a is: %c\n", a)
}

まとめ

このセクションで学んだことを振り返りましょう。

  • ASCII 文字は 1 バイトを占有し、128 種類の文字を表現できます。
  • UTF-8 エンコーディングは Unicode 文字セットの一種です。可変長エンコーディングであり、表現する文字によってバイト長が異なります。
  • byte データ型は ASCII 文字を表現するために使用され、rune データ型は Unicode 文字を表現するために使用されます。
  • byteuint8 の別名であり、runeint32 の別名です。

このセクションでは、まず ASCII、UTF-8、Unicode について説明しました。次に、文字データ型である byte および rune と、整数データ型との関係について解説しました。

✨ 解答を確認して練習