49 / 68

unicode va utf8

unicode va utf8 paketlari

Go tilida satrlar (string) ichida UTF-8 kodlashda saqlanadi. Bu esa qiziq bir holatga olib keladi: ba'zi belgilar (masalan, lotin harflari) 1 bayt egallasa, boshqalari (masalan, emoji yoki ba'zi maxsus belgilar) 2, 3 yoki 4 baytgacha joy egallashi mumkin.

Shuning uchun satrning bayt uzunligi (len(s)) har doim ham uning haqiqiy belgilar soniga teng bo'lavermaydi. Aynan shu farqni to'g'ri boshqarish uchun unicode/utf8 (kodlash bilan ishlash) va unicode (belgi turlarini aniqlash) paketlari kerak bo'ladi.

example.go
import (
    "unicode"
    "unicode/utf8"
)

1. Bayt uzunligi va haqiqiy belgilar soni farqi

example.go
package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    // ʻ — o'ng qo'shtirnoq belgisi (U+02BB), UTF-8'da 2 bayt egallaydi
    s := "gʻalati"

    fmt.Println("Bayt uzunligi:", len(s))
    fmt.Println("Belgilar soni:", utf8.RuneCountInString(s))

    emoji := "Salom 😊"
    fmt.Println("Bayt uzunligi:", len(emoji))
    fmt.Println("Belgilar soni:", utf8.RuneCountInString(emoji))
}

Natija:

kod
Bayt uzunligi: 8
Belgilar soni: 7
Bayt uzunligi: 10
Belgilar soni: 7

[!IMPORTANT] gʻalati so'zida 7 ta belgi bor, lekin ʻ belgisi 2 bayt egallagani uchun len() funksiyasi 8 ni qaytaradi. Agar siz Uzbek tilidagi matnlar (o', g' kabi maxsus belgili so'zlar) yoki emoji bilan ishlasangiz, hech qachon len(s)ni "belgilar soni" sifatida ishlatmang — buning o'rniga utf8.RuneCountInString(s)dan foydalaning.


2. Satr bo'ylab to'g'ri aylanish: for range

Goda for i, r := range s orqali satr bo'ylab aylanganda, i — belgining bayt indeksi (ketma-ket 0,1,2,... emas!), r esa haqiqiy belgi (rune turida, ya'ni Unicode kod nuqtasi):

example.go
package main

import "fmt"

func main() {
    for i, r := range "Go!" {
        fmt.Println(i, r, string(r))
    }
}

Natija:

kod
0 71 G
1 111 o
2 33 !

Bu yerda har bir belgi 1 baytli bo'lgani uchun indekslar ketma-ket (0, 1, 2) chiqdi. Agar satrda ko'p baytli belgilar bo'lganida, indekslar orasida "sakrashlar" bo'lar edi — chunki Go har safar keyingi TO'LIQ belgigacha o'tadi, keyingi baytga emas.


3. Satrni belgilar (rune) massiviga aylantirish

Satrni indeks orqali to'g'ridan-to'g'ri kesish (masalan, s[0:3]) bayt darajasida ishlaydi va ko'p baytli belgilarni "sindirib" qo'yishi mumkin. Xavfsiz ishlash uchun avval satrni []rune turiga aylantirish tavsiya etiladi:

example.go
package main

import "fmt"

func main() {
    runes := []rune("Salom")

    fmt.Println(runes)       // har bir belgining sonli (Unicode) qiymati
    fmt.Println(string(runes)) // orqaga satrga aylantiramiz
    fmt.Println(len(runes))    // endi bu HAQIQIY belgilar soni
}

Natija:

kod
[83 97 108 111 109]
Salom
5

[]rune massiviga aylantirilgandan so'ng, len() endi to'g'ri ishlaydi, chunki har bir element aynan bitta belgini ifodalaydi (bayt emas).


4. Belgi turini aniqlash: unicode.Is...()

unicode paketi bitta belgi (rune) qanday turga tegishli ekanligini (harf, raqam, bo'sh joy va h.k.) tekshirish uchun funksiyalar taqdim etadi:

example.go
package main

import (
    "fmt"
    "unicode"
)

func main() {
    fmt.Println(unicode.IsLetter('A')) // harfmi?
    fmt.Println(unicode.IsDigit('9'))  // raqammi?
    fmt.Println(unicode.IsSpace(' '))  // bo'sh joymi?
    fmt.Println(unicode.IsUpper('A'))  // katta harfmi?
    fmt.Println(unicode.IsLower('a'))  // kichik harfmi?
    fmt.Println(unicode.IsPunct('!'))  // tinish belgisimi?
}

Natija:

kod
true
true
true
true
true
true

Bu funksiyalar ko'pincha foydalanuvchi kiritgan matnni tekshirishda (masalan, parolda kamida bitta raqam borligini tekshirish) ishlatiladi.


5. Bitta belgini katta/kichik qilish: ToUpper() va ToLower()

strings.ToUpper()/ToLower() butun satr bilan ishlasa, unicode.ToUpper()/ToLower() xuddi shu ishni bitta rune (belgi) darajasida bajaradi:

example.go
package main

import (
    "fmt"
    "unicode"
)

func main() {
    fmt.Println(string(unicode.ToUpper('a')))
    fmt.Println(string(unicode.ToLower('A')))
}

Natija:

kod
A
a

6. Satr to'g'ri UTF-8 kodlanganligini tekshirish: utf8.ValidString()

Tashqi manbadan (masalan, tarmoqdan yoki fayldan) kelgan ma'lumot har doim ham to'g'ri UTF-8 formatida bo'lavermaydi. Buni tekshirish uchun:

example.go
package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    fmt.Println(utf8.ValidString("Go 学习")) // to'g'ri UTF-8 satrmi?
}

Natija:

kod
true

Xulosa: qachon qaysi funksiya kerak?

VazifaFunksiya
Haqiqiy belgilar sonini bilishutf8.RuneCountInString(s)
Satrni belgi-belgi qayta ishlashfor i, r := range s yoki []rune(s)
Belgi harfmi, raqammi, bo'sh joymiunicode.IsLetter(), IsDigit(), IsSpace()
Bitta belgini katta/kichik qilishunicode.ToUpper() / ToLower()
Satr to'g'ri UTF-8 ekanligini tekshirishutf8.ValidString()

[!NOTE] Qoida sifatida yodda tuting: len(s) — baytlar soni, utf8.RuneCountInString(s) — belgilar soni. Faqat ingliz alifbosidagi matnlar bilan ishlaganda ikkalasi bir xil chiqadi, lekin o'zbekcha maxsus belgilar (o', g'), kirillcha yoki emoji bilan ishlaganda bu farq muhim ahamiyat kasb etadi.