Metin dosyasını string dizisine oku (ve yaz)


101

Bir dizgi dizisinin içine ve dışına bir metin dosyasını okuma (ve yazma) yeteneği, oldukça yaygın bir gereksinim olduğuna inanıyorum. Bir veritabanına erişim ihtiyacını başlangıçta ortadan kaldıran bir dil ile başlarken de oldukça kullanışlıdır. Golang'da biri var mı?
Örneğin

func ReadLines(sFileName string, iMinLines int) ([]string, bool) {

ve

func WriteLines(saBuff[]string, sFilename string) (bool) { 

Yinelenmek yerine mevcut olanı kullanmayı tercih ederim.


2
Bir dosyadan satırları okumak için bufio.Scanner'ı kullanın, bkz. Stackoverflow.com/a/16615559/1136018 ve golang.org/pkg/bufio
Jack Valmadre

Yanıtlar:


125

Go1.1 sürümünden itibaren, bir dosyadaki satırları kolayca okuyabilen bir bufio.Scanner API vardır. Tarayıcı ile yeniden yazılan aşağıdaki örneği yukarıdan düşünün:

package main

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

// readLines reads a whole file into memory
// and returns a slice of its lines.
func readLines(path string) ([]string, error) {
    file, err := os.Open(path)
    if err != nil {
        return nil, err
    }
    defer file.Close()

    var lines []string
    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
        lines = append(lines, scanner.Text())
    }
    return lines, scanner.Err()
}

// writeLines writes the lines to the given file.
func writeLines(lines []string, path string) error {
    file, err := os.Create(path)
    if err != nil {
        return err
    }
    defer file.Close()

    w := bufio.NewWriter(file)
    for _, line := range lines {
        fmt.Fprintln(w, line)
    }
    return w.Flush()
}

func main() {
    lines, err := readLines("foo.in.txt")
    if err != nil {
        log.Fatalf("readLines: %s", err)
    }
    for i, line := range lines {
        fmt.Println(i, line)
    }

    if err := writeLines(lines, "foo.out.txt"); err != nil {
        log.Fatalf("writeLines: %s", err)
    }
}

124

Dosya çok büyük değilse, bu aşağıdaki gibi ioutil.ReadFileve strings.Splitişlevleriyle yapılabilir :

content, err := ioutil.ReadFile(filename)
if err != nil {
    //Do something
}
lines := strings.Split(string(content), "\n")

İoutil ve string paketleri hakkındaki belgeleri okuyabilirsiniz .


5
Dosyanın tamamını belleğe okur, bu dosya büyükse sorun olabilir.
jergason

22
@Jergason, bu yüzden cevabına "Dosya çok büyük değilse ..." diye başladı
laurent

9
ioutil şu şekilde içe aktarılabilir"io/ioutil"
Pramod

7
Not strings.Split düzenli POSIX metin dosyaları ayrıştırılırken bir ekstra satır (boş bir dize) ekler örnek
bain

1
Bilginize, Windows'ta, bu \r. Yani \rher öğeye bir eklemeniz olabilir .
matfax

32

İlk cevap güncellenemez.
Her neyse, Go1 sürümünden sonra bazı önemli değişiklikler oldu, bu yüzden aşağıda gösterildiği gibi güncelledim:

package main

import (
    "os"
    "bufio"
    "bytes"
    "io"
    "fmt"
    "strings"
)

// Read a whole file into the memory and store it as array of lines
func readLines(path string) (lines []string, err error) {
    var (
        file *os.File
        part []byte
        prefix bool
    )
    if file, err = os.Open(path); err != nil {
        return
    }
    defer file.Close()

    reader := bufio.NewReader(file)
    buffer := bytes.NewBuffer(make([]byte, 0))
    for {
        if part, prefix, err = reader.ReadLine(); err != nil {
            break
        }
        buffer.Write(part)
        if !prefix {
            lines = append(lines, buffer.String())
            buffer.Reset()
        }
    }
    if err == io.EOF {
        err = nil
    }
    return
}

func writeLines(lines []string, path string) (err error) {
    var (
        file *os.File
    )

    if file, err = os.Create(path); err != nil {
        return
    }
    defer file.Close()

    //writer := bufio.NewWriter(file)
    for _,item := range lines {
        //fmt.Println(item)
        _, err := file.WriteString(strings.TrimSpace(item) + "\n"); 
        //file.Write([]byte(item)); 
        if err != nil {
            //fmt.Println("debug")
            fmt.Println(err)
            break
        }
    }
    /*content := strings.Join(lines, "\n")
    _, err = writer.WriteString(content)*/
    return
}

func main() {
    lines, err := readLines("foo.txt")
    if err != nil {
        fmt.Println("Error: %s\n", err)
        return
    }
    for _, line := range lines {
        fmt.Println(line)
    }
    //array := []string{"7.0", "8.5", "9.1"}
    err = writeLines(lines, "foo2.txt")
    fmt.Println(err)
}

18

Bunun için bufio paketi ile os.File'ı ( io.Reader arayüzünü uygulayan ) kullanabilirsiniz. Ancak, bu paketler sabit bellek kullanımı göz önünde bulundurularak (dosya ne kadar büyük olursa olsun) oluşturulmuştur ve oldukça hızlıdır.

Ne yazık ki bu, tüm dosyanın belleğe okunmasını biraz daha karmaşık hale getirir. Satır sınırını aşmaları halinde, satırın bölümlerini birleştirmek için bir bytes.Buffer kullanabilirsiniz . Her neyse, satır okuyucuyu doğrudan projenizde kullanmayı denemenizi tavsiye ederim (özellikle metin dosyasının ne kadar büyük olduğunu bilmiyorsanız!). Ancak dosya küçükse, aşağıdaki örnek sizin için yeterli olabilir:

package main

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

// Read a whole file into the memory and store it as array of lines
func readLines(path string) (lines []string, err os.Error) {
    var (
        file *os.File
        part []byte
        prefix bool
    )
    if file, err = os.Open(path); err != nil {
        return
    }
    reader := bufio.NewReader(file)
    buffer := bytes.NewBuffer(make([]byte, 1024))
    for {
        if part, prefix, err = reader.ReadLine(); err != nil {
            break
        }
        buffer.Write(part)
        if !prefix {
            lines = append(lines, buffer.String())
            buffer.Reset()
        }
    }
    if err == os.EOF {
        err = nil
    }
    return
}

func main() {
    lines, err := readLines("foo.txt")
    if err != nil {
        fmt.Println("Error: %s\n", err)
        return
    }
    for _, line := range lines {
        fmt.Println(line)
    }
}

Diğer bir alternatif , dosyanın tamamını bir defada okumak için io.ioutil.ReadAll kullanmak ve daha sonra dilimlemeyi satır satır yapmak olabilir. Size satırları dosyaya nasıl geri yazacağınıza dair açık bir örnek vermiyorum, ancak bu temelde os.Create()örnektekine benzer bir döngü tarafından izlenen bir döngüdür (bakınız main()).


Bu bilgi için teşekkürler. Tüm işi yapmak için mevcut bir paketi kullanmakla daha çok ilgileniyordum, çünkü oldukça faydalı olduğunu düşünüyorum. Örneğin, Go'yu başlangıçta bir veritabanı kullanmadan verilerin kalıcılığı ile kullanmak istiyorum. Bazı dillerde buna inanıyorum. Örneğin. Ruby'nin bir dizi dizgeyi (bellekten) okuyan Readlines olduğunu düşünüyorum - özellikle Ruby hayranı olduğumdan değil. Sanırım önemli değil, sadece çoğaltmayı sevmiyorum, ama belki de onu isteyen sadece bendim. Her neyse, bunu yapmak için bir paket yazdım ve belki de github'a koyacağım. Bu dosyalar genellikle çok küçüktür.
brianoh

Herhangi bir tür go yapısını (örneğin bir dizi dizge, tamsayı, harita veya daha karmaşık yapılar) sürdürmek istiyorsanız, bunun gob.Encode()için basitçe kullanabilirsiniz . Sonuç, satırsonu ile ayrılmış metin dosyası yerine ikili bir dosyadır. Bu dosya her türlü veriyi içerebilir, verimli bir şekilde ayrıştırılabilir, elde edilen dosya daha küçük olur ve bu satırlar ve dinamik ayırma ile uğraşmanıza gerek kalmaz. Bu nedenle, bir şeyi daha sonra Go ile kullanmak için ısrar etmek istiyorsanız muhtemelen sizin için daha uygun olacaktır.
tux21b

İstediğim şey, herhangi bir satırı (alanı) değiştirebilmem için bir dizi metin satırı. Bu dosyalar çok küçük. Değişiklikler yapıldığında, değişken uzunluklu dizeler sonunda geri yazılır. Yapmak istediğim şey için çok esnek ve hızlı. Satırları (alanları) ayırmak için yeni satırlara ihtiyacım var. Belki daha iyi bir yol vardır, ancak şu anki amaçlarım için bu uygun görünüyor. Önerdiklerine daha sonra bakacağım ve belki sonra değiştireceğim.
brianoh

2
R58 (Temmuz 2011) itibariyle kodlama / satır paketinin kaldırıldığına dikkat edin. "İşlevselliği artık bufio'da."
kristianp

4
func readToDisplayUsingFile1(f *os.File){
    defer f.Close()
    reader := bufio.NewReader(f)
    contents, _ := ioutil.ReadAll(reader)
    lines := strings.Split(string(contents), '\n')
}

veya

func readToDisplayUsingFile1(f *os.File){
    defer f.Close()
    slice := make([]string,0)

    reader := bufio.NewReader(f)

    for{

    str, err := reader.ReadString('\n')
    if err == io.EOF{
        break
    }

        slice = append(slice, str)
    }

1
Herkes ne kadar "modern" olursa Go olduğunu söylemeye çalışırsa, o kadar çok 35 yıllık minimum kütüphane bağlama kodu gibi görünür. : \ Satır tabanlı bir metin dosyasını basitçe okumanın böyle bir karmaşa olması, Go'nun daha genel bir amaç için .... gitmek için uzun bir yolu olduğunu pekiştiriyor. Diğer dillerde ve platformlarda hala çok verimli bir şekilde işlenen çok sayıda metin, satır tabanlı veri var. $ 0,02
ChrisH
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.