Skip to content

Latest commit

 

History

History
198 lines (146 loc) · 8.4 KB

3_file.md

File metadata and controls

198 lines (146 loc) · 8.4 KB

Файл ба Хавтас

Файл нь өгөгдөл хадгалах үндсэн нэгж юм. Файлыг байтуудын цуваа гэж үзэж болно. Файл нь ихэнхидээ диск төхөөрөмж дээр байрлана, гэхдээ зарим тохиолдолд терминал, хэвлэгч, соронзон тууз зэргийг файл гэж үздэг.

Файлын тухай судлахын өмнө Go хэлний io пакетыг ойлгох хэрэгтэй. Энэ пакет нь цөөн тооны функцтэй, ихэвчлэн интерфэйсийн тодорхойлолт агуулсан байдаг. Хамгийн чухал хоёр интерфэйс нь Reader болон Writer. Reader нь Read методоор дамжуулах унших, Writer нь Write методоор дамжуулан бичих боломжоор хангана.

Эдгээр уншигч, бичигч нь бусад функцэд өргөн хэрэглэгддэг. Тухайлбал io сангийн Copy() функц нь Reader, Writer төрлийн параметрүүд хүлээн авдаг:

func Copy(dst Writer, src Reader) (written int64, err error)

Файлтай ажиллах

Файл нээхийн тулд os.Open() функцийг ашиглана. Файлаас өгөгдөл уншиж дэлгэц дээр харуулах кодыг доор харуулав:

package main

import (
  "fmt"
  "os"
)

func main() {
    file, err := os.Open("test.txt")
    if err != nil {
        // алдааг энд боловсруулна
        return
    }
    defer file.Close()

    // файлын хэмжээг авах
    stat, err := file.Stat()
    if err != nil {
        return
    }
    // файлыг унших
    bs := make([]byte, stat.Size())
    _, err = file.Read(bs)
    if err != nil {
        return
    }

    str := string(bs)
    fmt.Println(str)
}

Файлыг нээсний дараа defer file.Close() зааврыг бичсэн байна. Энэ заавар нь функцийн ажиллагаа дуусмагц файлыг хаах болно.

Файл унших үйлдэл маш олон давтагддаг учраас арай хялбар арга бас бий. Тухайлбал ioutil.ReadFile() функц нь файл уншилтыг ихээхэн хялбар болгодог.

package main

import (
  "fmt"
  "io/ioutil"
)

func main() {
    bs, err := ioutil.ReadFile("test.txt")
    if err != nil {
        return
    }
    str := string(bs)
    fmt.Println(str)
}

Файл үүсгэх:

package main

import "os"

func main() {
    file, err := os.Create("test.txt")
    if err != nil {
        // алдааг энд боловсруулна
        return
    }
    defer file.Close()

    file.WriteString("test")
}

Хавтасны агуулгыг уншихдаа мөн os.Open() функцийг ашиглана, гэхдээ файлын нэрний оронд хавтасын замыг заах хэрэгтэй. Нээсний дараа Readdir() функцийг дуудна:

package main

import (
  "fmt"
  "os"
)

func main() {
    dir, err := os.Open(".")
    if err != nil {
        return
    }
    defer dir.Close()

    fileInfos, err := dir.Readdir(-1)
    if err != nil {
        return
    }
    for _, fi := range fileInfos {
        fmt.Println(fi.Name())
    }
}

Хавтас унших үед ихэвчлэн дэд хавтас руу орж рекурсивээр давтах шаардлага гардаг. Үүнийг хялбарчлах зорилгоор Walk() функцийг ашиглаж болно.

package main

import (
  "fmt"
  "os"
  "path/filepath"
)

func main() {
  // Walk функцэд дамжуулсан функц нь бүх хавтасын хувьд дуудагдана
  filepath.Walk(".",
    func(path string, info os.FileInfo, err error) error {
        fmt.Println(path)
        return nil
    })
}

Форматтай уншиж, бичих

Файлд өгөгдлийг форматтай бичихэд fmt.Fprintf(), fmt.Sprintf() функцуудыг ашиглаж болно. Эдгээр функц нь бидний мэдэх fmt.Printf() функцтэй ижил зарчмаар ажилладаг. Ялгаа нь эхний параметр файл эсвэл санах ойн буфер рүү форматлаж бичдэг. Файл руу бичих бол fmt.Fprintf, тэмдэгт мөр буфер руу бичих бол fmt.Sprintf функцийг ашиглана.

Жишээ нь:

fmt.Sprintf("%[2]d %[1]d\n", 11, 22)

fmt.Scanf функцтэй төстэй мөн fmt.Fscanf, fmt.Sscanf хувилбарууд бий.

Буферт оролт/гаралт

Буфертэй оролт/гаралт (Input/Output буюу товчоор I/O) нь шууд файл руу бичилт хийхгүй. Харин өгөгдөл нь бичихэд хангалттай том болтол буферт хадгалж байгаад бичилт хийдэг. Эсвэл зориудаар буферыг шавхаж бичиж болно.

Ихэнх тохиолдолд буфертэй I/O ашиглах хэрэгтэй. Буфергүй I/O хийх бүрт системийн дуудалт хийдэг. Үйлдлийн систем рүү хийж байгаа дуудалт бүр нь үнэтэй зүйл юм. Буфертэй I/O нь энэ дуудалтыг багасгана.

Төсөөлвөл, шалан дээр унагасан цаасуудыг цуглуулж байна гэж бодоё, үүнийг буфертэй болон буфергүй горимд хийж болно. Буфергүй горимд цаасуудыг нэг нэгээр нь авч ширээн дээр тавина гэсэн үг. Ингэвэл олон дахин доош тонгойх, дээш босох хэрэг гарна. Буфертэй горимд баруун гараараа цаасуудыг түүж, түүнийгээ зүүн гар (буфер) дээрээ хураана. Ингээд зүүн гар дүүрсэн буюу дааж чадах хязгаарт хүрсэн үед тэдгээрийг ширээн дээр тавина. Энэ тохиолдолд мэдээж цөөн удаа тонгойх, босох учраас зөв шийдэл болж байна.

bytes.Buffer

Өгөгдлийг буферт хураах шаардлагатай үед bytes сангийн Buffer обектыг ашиглаж болно:

var buf bytes.Buffer
buf.Write([]byte("test"))

Буферээс буцаан өгөгдлөө гаргаж авах бол buf.Bytes() гэж дуудна, тэмдэгт мөр гаргаж авах бол strings хувиргагч ашиглаж болно. Эсвэл буферын WriteTo() функцээр файл болон бусад төрлийн урсгал руу шууд бичиж болно.

buf.WriteTo(os.Stdout)

bufio пакет

bufio пакет нь буфертэй оролт, гаралт хийхэд зориулагдсан байдаг. Ө.х автоматаар цаанаа буфер ашигладаг гэсэн үг.

package main

import (
  "bufio"
  "fmt"
  "os"
)

func main() {
    w := bufio.NewWriter(os.Stdout)
    fmt.Fprint(w, "Hello, ")
    fmt.Fprint(w, "world!")
    w.Flush() // шавхах
}

Буферт байгаа өгөгдөл дүүрэх эсвэл зориудаар Flush() функц дуудах хүртэл өгөгдөл буферт хураагдана. Энэ нь эцсийн төхөөрөмж рүү бичилтийн тоог цөөлөх ач холбогдолтой.

Ихэнхи тохиолдолд буфер ашиглах нь зөв байдаг. Гэхдээ зарим тохиолдолд буфер ашиглахаас татгалзах хэрэгтэй. Жишээ нь програмын алдааг дэлгэцэнд мэдээлэхэд буфер ашиглах нь тохиромжгүй байж болно. Учир нь алдааг гарсан дариуд нь хэрэглэгч мэдэх хэрэгтэй шүү дээ. Эсвэл ойр ойрхон Flush() функцийг дуудаж байх хэрэгтэй.