Strings and Runes

Strings and Runes #

// Go string ဆိုတာ read-only byte slice တစ်ခုဖြစ်ပါတယ်။ Go language နဲ့
// standard library က string တွေကို encoded [UTF-8](https://en.wikipedia.org/wiki/UTF-8) (containers of text) အဖြစ်ဖြစ်
// special အနေဖြင့်အသုံးပြုပါတယ်။
// တခြား language တွေမှာ string တွေက "character" တွေနဲ့ ဖွဲ့စည်းထားပါတယ်။
// Go မှာတော့ character ဆိုတဲ့ သဘောတရားကို `rune` လို့ခေါ်ပါတယ် -
// Unicode code point တစ်ခုကို ကိုယ်စားပြုတဲ့ integer တစ်ခုပါ။
// [ဒီ Go blog post](https://go.dev/blog/strings) က ဒီအကြောင်းအရာနှင့်ပတ်သတ်တာကိုသေချာ မိတ်ဆက်ထားပါတယ်။

package main

import (
	"fmt"
	"unicode/utf8"
)

func main() {

	// `s` က ထိုင်းဘာသာစကားဖြင့် "hello" လို့ရေးထားတဲ့ literal value ကို assign လုပ်ထားတဲ့
	// `string` တစ်ခုဖြစ်ပါတယ်။ Go string literal တွေဟာ UTF-8 နဲ့
	// encode လုပ်ထားတဲ့ စာသားတွေဖြစ်ပါတယ်။
	const s = "สวัสดี"

	// String တွေဟာ `[]byte` နဲ့ တူညီတဲ့အတွက်၊ ဒီကုဒ်က
	// သိမ်းဆည်းထားတဲ့ raw byte တွေရဲ့ အရေအတွက်ကို ပြပါလိမ့်မယ်။
	fmt.Println("Len:", len(s))

	// String ထဲကို indexing လုပ်ခြင်းဟာ တစ်ခုချင်းစီရဲ့ index မှာရှိတဲ့ raw byte တန်ဖိုးတွေကို ထုတ်ပေးပါတယ်။
	// ဒီ loop က `s` ထဲမှာပါတဲ့ code point တွေကို ဖွဲ့စည်းထားတဲ့ byte အားလုံးရဲ့ hex တန်ဖိုးတွေကို ထုတ်ပေးပါတယ်။
	for i := 0; i < len(s); i++ {
		fmt.Printf("%x ", s[i])
	}
	fmt.Println()

	// String တစ်ခုထဲမှာ *rune* ဘယ်နှစ်ခုပါလဲဆိုတာ ရေတွက်ဖို့ `utf8` package ကို သုံးနိုင်ပါတယ်။
	// `RuneCountInString` ရဲ့ run-time က string ရဲ့ အရွယ်အစားပေါ်မှာ မူတည်ပါတယ်။
	// ဘာကြောင့်လဲဆိုတော့ UTF-8 rune တိုင်းကို တစ်ခုချင်းစီ decode လုပ်ရလို့ပါ။
	// ထိုင်းစာလုံးအချို့ဟာ UTF-8 code point တွေအများကြီးနဲ့ ဖွဲ့စည်းထားတာမို့
	// ဒီရေတွက်မှုရဲ့ ရလဒ်က အံ့ဩစရာ (surprising) ဖြစ်နိုင်ပါတယ်။
	fmt.Println("Rune count:", utf8.RuneCountInString(s))

	// `range` loop က string တွေကို
	// `rune` တိုင်းကို သူ့ရဲ့ string ထဲက offset နဲ့အတူ decode လုပ်ပီး handle လုပ်ပါတယ်။
	for idx, runeValue := range s {
		fmt.Printf("%#U starts at %d\n", runeValue, idx)
	}

	// `utf8.DecodeRuneInString` function ကို အသုံးပြုပြီးလည်း
	// အထက်ကလိုမျိုး iteration လုပ်လို့ရပါတယ်။
	fmt.Println("\nUsing DecodeRuneInString")
	for i, w := 0, 0; i < len(s); i += w {
		runeValue, width := utf8.DecodeRuneInString(s[i:])
		fmt.Printf("%#U starts at %d\n", runeValue, i)
		w = width

		// ဒါက `rune` တန်ဖိုးတစ်ခုကို function ဆီသို့ pass လုပ်တာကို ပြသပါတယ်။
		examineRune(runeValue)
	}
}

func examineRune(r rune) {
	// Single quote နဲ့ ဝိုင်းထားတဲ့ value တွေက rune literals ဖြစ်ပါတယ်။
	// `rune` တန်ဖိုးတစ်ခုကို rune literal နဲ့ တိုက်ရိုက်နှိုင်းယှဉ်လို့ရပါတယ်။
	if r == 't' {
		fmt.Println("found tee")
	} else if r == 'ส' {
		fmt.Println("found so sua")
	}
}
$ go run strings-and-runes.go
Len: 18
e0 b8 aa e0 b8 a7 e0 b8 b1 e0 b8 aa e0 b8 94 e0 b8 b5 
Rune count: 6
U+0E2A 'ส' starts at 0
U+0E27 'ว' starts at 3
U+0E31 'ั' starts at 6
U+0E2A 'ส' starts at 9
U+0E14 'ด' starts at 12
U+0E35 'ี' starts at 15

Using DecodeRuneInString
U+0E2A 'ส' starts at 0
found so sua
U+0E27 'ว' starts at 3
U+0E31 'ั' starts at 6
U+0E2A 'ส' starts at 9
found so sua
U+0E14 'ด' starts at 12
U+0E35 'ี' starts at 15