Go با مثال: رشته‌ها و رون‌ها

رشته در Go یک اسلایس فقط‌خواندنی از بایت‌هاست. زبان و کتابخانهٔ استاندارد با رشته‌ها رفتار ویژه‌ای دارند و آن‌ها را ظرفی برای متن کدگذاری‌شده با UTF-8 می‌دانند. در زبان‌های دیگر، رشته‌ها از «کاراکتر» تشکیل می‌شوند. در Go، مفهوم کاراکتر rune (رون) نام دارد؛ یک عدد صحیح که نمایانگر یک کدپوینت یونیکد است. این مقاله از وبلاگ Go مقدمهٔ خوبی بر این موضوع است.

package main
import (
    "fmt"
    "unicode/utf8"
)
func main() {

s یک string است که مقداری لفظی به آن داده شده؛ کلمهٔ «سلام» به زبان تایلندی. رشته‌های لفظی در Go متن کدگذاری‌شده با UTF-8 هستند.

    const s = "สวัสดี"

چون رشته‌ها معادل []byte هستند، این کد طول بایت‌های خامِ ذخیره‌شده در آن را می‌دهد.

    fmt.Println("Len:", len(s))

اندیس‌گذاری روی رشته، مقدار بایت خام در هر اندیس را می‌دهد. این حلقه مقدار هگز همهٔ بایت‌هایی را که کدپوینت‌های s را تشکیل می‌دهند تولید می‌کند.

    for i := 0; i < len(s); i++ {
        fmt.Printf("%x ", s[i])
    }
    fmt.Println()

برای شمردن تعداد رون‌های یک رشته می‌توانیم از بستهٔ utf8 استفاده کنیم. توجه کنید که زمان اجرای RuneCountInString به اندازهٔ رشته بستگی دارد، چون باید تک‌تک رون‌های UTF-8 را به ترتیب رمزگشایی کند. برخی کاراکترهای تایلندی با کدپوینت‌هایی نمایش داده می‌شوند که چند بایت را در بر می‌گیرند، پس نتیجهٔ این شمارش ممکن است غافلگیرکننده باشد.

    fmt.Println("Rune count:", utf8.RuneCountInString(s))

حلقهٔ range با رشته‌ها رفتار ویژه‌ای دارد و هر rune را همراه با موقعیتش در رشته رمزگشایی می‌کند.

    for idx, runeValue := range s {
        fmt.Printf("%#U starts at %d\n", runeValue, idx)
    }

همین پیمایش را می‌توانیم با استفادهٔ صریح از تابع utf8.DecodeRuneInString هم انجام دهیم.

    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 به یک تابع را نشان می‌دهد.

        examineRune(runeValue)
    }
}
func examineRune(r rune) {

مقادیری که بین کوتیشن تکی قرار می‌گیرند رون لفظی (rune literal) هستند. می‌توانیم یک مقدار rune را مستقیماً با یک رون لفظی مقایسه کنیم.

    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

مثال بعدی: .