テキストファイルを文字列配列に読み込み(そして書き込み)


100

文字列配列にテキストファイルを読み書きする機能は、かなり一般的な要件だと思います。また、最初にデータベースにアクセスする必要をなくす言語から始める場合にも非常に役立ちます。Golangには存在しますか?
例えば

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

そして

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

複製ではなく、既存のものを使用したいと思います。


2
ファイルから行を読み取りに使用bufio.Scanner、参照stackoverflow.com/a/16615559/1136018golang.org/pkg/bufio
ジャックValmadre

回答:


123

Go1.1リリース以降、ファイルから行を簡単に読み取ることができるbufio.Scanner APIがあります。上記の次の例を検討してください。スキャナーで書き直されています。

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

ファイルが大きすぎない場合は、ioutil.ReadFileand strings.Split関数を使用して次のようにできます。

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

ioutilおよびstringsパッケージに関するドキュメントを読むことができます。


5
ファイル全体がメモリに読み込まれるため、ファイルが大きい場合は問題になる可能性があります。
ジャーガソン2013年

22
@Jergason、それが彼が彼の答えを「ファイルが大きすぎないなら...」から始めた理由です
laurent

9
ioutilは次のようにインポートできます"io/ioutil"
Pramod

7
文字列に注意してください。Splitは、通常のPOSIXテキストファイルの例を
bain

1
参考までに、Windowsでは、これは削除されません\r。したがって\r、すべての要素にアペンドすることができます。
matfax

32

最初の回答を更新できません。
とにかく、Go1のリリース後、いくつかの重大な変更があるため、以下のように更新しました。

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

そのためのbufioパッケージでos.Fileio.Readerインターフェイスを実装)を使用できます。ただし、これらのパッケージは、メモリの使用量が固定されていることを念頭に置いてビルドされており(ファイルの大きさに関係なく)、非常に高速です。

残念ながら、これによりファイル全体をメモリに読み込むのが少し複雑になります。bytes.Bufferを使用すると、行の制限を超えた場合に行の一部を結合できます。とにかく、プロジェクトでラインリーダーを直接使用することをお勧めします(特に、テキストファイルのサイズがわからない場合は!)。ただし、ファイルが小さい場合は、次の例で十分です。

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)
    }
}

別の方法としては、io.ioutil.ReadAllを使用してファイル全体を一度に読み取り、後で行単位でスライスすることもできます。行をファイルに書き戻す方法の明示的な例は示しませんが、基本的にos.Create()は、例のループに似たループが続きます(を参照main())。


その情報をありがとう。既存のパッケージを使用して仕事全体を行うことに興味がありました。これは非常に便利だと思うからです。たとえば、最初はデータベースを使用せずに、データを永続化してGoを使用したいと考えています。一部の言語にはこれがあると私は信じています。例えば。Rubyには文字列の配列を(メモリから)読み取るReadlinesがあると思います-特にRubyのファンではありません。それは大したことではないと思います、私は複製が好きではありませんが、多分それが欲しいのは私だけです。とにかく、私はそれを行うためのパッケージを書きました、そしておそらく私はそれをgithubに置くでしょう。これらのファイルは通常非常に小さいです。
brianoh '

任意の種類のgo構造(たとえば、文字列、整数、マップ、またはより複雑な構造の配列)を単純に永続化したい場合は、単にgob.Encode()forを使用できます。結果は、改行で区切られたテキストファイルではなく、バイナリファイルになります。このファイルにはあらゆる種類のデータを含めることができ、効率的に解析できます。結果のファイルは小さくなり、改行や動的割り当てを処理する必要はありません。したがって、後でGoで使用するために何かを永続化したい場合は、おそらくより適しています。
tux21b

必要なのは、任意の行(フィールド)を変更できるように、テキスト行の配列です。これらのファイルは非常に小さいです。変更が加えられると、可変長文字列が最終的に書き戻されます。それは私がやりたいことに非常に柔軟で高速です。行(フィールド)を区切るには改行が必要です。おそらくもっと良い方法があるかもしれませんが、これは現在のところ私の目的には問題ないようです。私はあなたの提案を後で見て、おそらくそれを変更します。
brianoh

2
r58(2011年7月)以降、encoding / lineパッケージは削除されました。「その機能は現在bufioにあります。」
kristianp '09

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

または

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
誰もが "モダン"にGoを言い続けようとするほど、35歳の最小限のライブラリバインディングコードのように見えます。:\行ベースのテキストファイルを単に読み取るだけで混乱するという事実は、Goがより一般的な目的に到達するまでの長い道のりがあることを強調するだけです。他の言語やプラットフォームで非常に効率的に処理されているテキストや行ベースのデータがたくさんあります。$ .02
ChrisH
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.