58 / 68

io va bufio

io va bufio paketlari

Goda fayl, tarmoq ulanishi, konsol yoki hatto xotiradagi matn — bularning barchasi bir xil umumiy tushuncha orqali ifodalanadi: oqim (stream). io paketi shu oqimlar bilan ishlash uchun umumiy interfeyslarni (Reader, Writer) belgilaydi, bufio esa ularni buferlash orqali tezroq va qulayroq qiladi (masalan, qator-qator o'qish).

example.go
import (
    "bufio"
    "io"
)

1. Godagi eng muhim ikkita interfeys: io.Reader va io.Writer

io paketining yuragi — bu ikkita juda oddiy interfeys:

example.go
type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

Fayl, tarmoq ulanishi, strings.Builder, bytes.Buffer, hatto os.Stdout — bularning barchasi shu ikki interfeysni amalga oshiradi. Shu sababli, agar sizning funksiyangiz io.Writer qabul qilsa, u bir xil kodni fayl, tarmoq yoki oddiy matn buferiga yozish uchun ishlata oladi — manba/manzil qayerdaligi funksiya uchun muhim emas.


2. Butun oqimni bir yo'la o'qish: io.ReadAll()

example.go
package main

import (
    "fmt"
    "io"
    "strings"
)

func main() {
    // strings.NewReader — oddiy satrni io.Reader ko'rinishiga aylantiradi
    reader := strings.NewReader("Salom, Go dunyosi!")

    data, err := io.ReadAll(reader)

    fmt.Println(string(data))
    fmt.Println(err)
}

Natija:

kod
Salom, Go dunyosi!
<nil>

[!NOTE] os.ReadFile() (oldingi darsda ko'rgan edik) ichki jarayonda aynan io.ReadAll()dan foydalanadi. Farqi shundaki, os.ReadFile() faqat fayllar uchun mo'ljallangan qulay "qisqartma", io.ReadAll() esa istalgan io.Reader (fayl, tarmoq, satr...) bilan ishlaydi.


3. Bir oqimdan ikkinchisiga nusxalash: io.Copy()

example.go
package main

import (
    "fmt"
    "io"
    "strings"
)

func main() {
    src := strings.NewReader("Nusxalash uchun matn")

    var dst strings.Builder

    // src dagi hamma narsani dst ga ko'chiradi
    n, err := io.Copy(&dst, src)

    fmt.Println(dst.String())
    fmt.Println("Ko'chirilgan bayt:", n)
    fmt.Println(err)
}

Natija:

kod
Nusxalash uchun matn
Ko'chirilgan bayt: 20
<nil>

io.Copy() katta fayllarni nusxalashda ham juda samarali ishlaydi, chunki u butun faylni xotiraga yuklamasdan, kichik-kichik bo'laklarda o'qib-yozib boradi.


4. Matnni qator-qator o'qish: bufio.Scanner

Foydalanuvchi kiritgan matnni yoki faylni qator-qator (yoki so'z-so'z) o'qishning eng qulay usuli — bufio.Scanner:

example.go
package main

import (
    "bufio"
    "fmt"
    "strings"
)

func main() {
    text := "Birinchi qator\nIkkinchi qator\nUchinchi qator"

    scanner := bufio.NewScanner(strings.NewReader(text))

    // Scan() har chaqirilganda navbatdagi qatorga o'tadi, qator qolmaganda false qaytaradi
    for scanner.Scan() {
        fmt.Println("Qator:", scanner.Text())
    }
}

Natija:

kod
Qator: Birinchi qator
Qator: Ikkinchi qator
Qator: Uchinchi qator

[!IMPORTANT] Aynan shu bufio.Scanner — konsoldan (os.Stdin) foydalanuvchi ma'lumotini qator-qator o'qishning eng tabiiy usuli: bufio.NewScanner(os.Stdin). Bu fmt.Scanln()dan farqli o'laroq, bo'sh joy ichida bo'lgan butun qatorni (masalan, "Ali Valiyev" kabi ism-familiyani) bitta kirish sifatida o'qiy oladi.

Scannerni so'z-so'z o'qish uchun ham sozlash mumkin:

example.go
wordScanner := bufio.NewScanner(strings.NewReader("Go juda qiziqarli til"))
wordScanner.Split(bufio.ScanWords) // standart holat qator-qator, buni so'z-so'zga o'zgartiramiz

for wordScanner.Scan() {
    fmt.Println(wordScanner.Text())
}

Natija:

kod
Go
juda
qiziqarli
til

5. Ma'lum bir belgigacha o'qish: bufio.Reader

Agar qator emas, balki istalgan boshqa belgigacha (masalan, vergulgacha) o'qish kerak bo'lsa, bufio.Reader ishlatiladi:

example.go
package main

import (
    "bufio"
    "fmt"
    "strings"
)

func main() {
    r := bufio.NewReader(strings.NewReader("Salom dunyo\n"))

    // '\n' belgisi uchrashiga qadar (shu belgi bilan birga) o'qiydi
    line, err := r.ReadString('\n')

    fmt.Printf("%q\n", line)
    fmt.Println(err)
}

Natija:

kod
"Salom dunyo\n"
<nil>

6. Buferlangan yozish: bufio.Writer

Ko'p marta kichik-kichik yozish amalini bajarish (masalan, faylga har bir qatorni alohida yozish) sekin ishlashi mumkin, chunki har safar haqiqiy "yozish" amali sodir bo'ladi. bufio.Writer yozuvlarni avval xotirada to'playdi, so'ng bitta katta bo'lak sifatida jo'natadi:

example.go
package main

import (
    "bufio"
    "fmt"
    "strings"
)

func main() {
    var buf strings.Builder

    w := bufio.NewWriter(&buf)
    w.WriteString("bufio orqali yozilgan")

    // Flush() chaqirilmaguncha ma'lumot buferda "kutib" turadi!
    w.Flush()

    fmt.Println(buf.String())
}

Natija:

kod
bufio orqali yozilgan

[!WARNING] bufio.Writer bilan ishlaganda Flush()ni chaqirishni unutmang! Aks holda, buferga yig'ilgan ma'lumot haqiqiy manzilga (fayl, konsol) hech qachon yozilmasligi mumkin — dastur tugaganda ham bufer "bo'shatilmasdan" qolib ketishi mumkin.


Xulosa: qachon qaysi funksiya kerak?

VazifaFunksiya
Istalgan oqimdan hammasini bir yo'la o'qishio.ReadAll()
Bir oqimdan boshqasiga nusxalashio.Copy()
Konsol yoki fayldan qator-qator o'qishbufio.NewScanner()
So'z-so'z o'qishscanner.Split(bufio.ScanWords)
Ma'lum belgigacha o'qishbufio.NewReader().ReadString(belgi)
Tez-tez kichik yozuvlarni samarali yig'ishbufio.NewWriter() + Flush()