Goを使用してファイルを読み書きする方法は?


284

私はGoを自分で習得しようと努めてきましたが、通常のファイルの読み取りと書き込みを試みることに困惑してきました。

まで取得できますinFile, _ := os.Open(INFILE, 0, 0)が、実際にはファイルのコンテンツを取得しても意味がありません。read関数がa []byteをパラメーターとして取るためです。

func (file *File) Read(b []byte) (n int, err Error)

回答:


476

Goでファイルを読み書きするすべての方法のGo 1互換リストを作成しましょう。

ファイルAPIが最近変更されたため、他のほとんどの回答はGo 1では機能しませんbufio。重要なIMHO も見逃しています。

次の例では、ファイルを読み取り、宛先ファイルに書き込むことによってファイルをコピーします。

基本から始める

package main

import (
    "io"
    "os"
)

func main() {
    // open input file
    fi, err := os.Open("input.txt")
    if err != nil {
        panic(err)
    }
    // close fi on exit and check for its returned error
    defer func() {
        if err := fi.Close(); err != nil {
            panic(err)
        }
    }()

    // open output file
    fo, err := os.Create("output.txt")
    if err != nil {
        panic(err)
    }
    // close fo on exit and check for its returned error
    defer func() {
        if err := fo.Close(); err != nil {
            panic(err)
        }
    }()

    // make a buffer to keep chunks that are read
    buf := make([]byte, 1024)
    for {
        // read a chunk
        n, err := fi.Read(buf)
        if err != nil && err != io.EOF {
            panic(err)
        }
        if n == 0 {
            break
        }

        // write a chunk
        if _, err := fo.Write(buf[:n]); err != nil {
            panic(err)
        }
    }
}

ここで使用os.Openos.Createた、便利なラッパーos.OpenFileです。通常、OpenFile直接電話する必要はありません。

EOFの扱いに注意してください。呼び出しごとReadに埋めようとし、そうすることでファイルの終わりに達した場合はエラーとしてbuf返しますio.EOF。この場合bufでもデータは保持されます。後続の呼び出しは、Read読み取られたバイト数としてゼロを返しio.EOF、エラーと同じです。その他のエラーはパニックにつながります。

使用する bufio

package main

import (
    "bufio"
    "io"
    "os"
)

func main() {
    // open input file
    fi, err := os.Open("input.txt")
    if err != nil {
        panic(err)
    }
    // close fi on exit and check for its returned error
    defer func() {
        if err := fi.Close(); err != nil {
            panic(err)
        }
    }()
    // make a read buffer
    r := bufio.NewReader(fi)

    // open output file
    fo, err := os.Create("output.txt")
    if err != nil {
        panic(err)
    }
    // close fo on exit and check for its returned error
    defer func() {
        if err := fo.Close(); err != nil {
            panic(err)
        }
    }()
    // make a write buffer
    w := bufio.NewWriter(fo)

    // make a buffer to keep chunks that are read
    buf := make([]byte, 1024)
    for {
        // read a chunk
        n, err := r.Read(buf)
        if err != nil && err != io.EOF {
            panic(err)
        }
        if n == 0 {
            break
        }

        // write a chunk
        if _, err := w.Write(buf[:n]); err != nil {
            panic(err)
        }
    }

    if err = w.Flush(); err != nil {
        panic(err)
    }
}

bufioデータとは関係がないため、ここではバッファとして機能しています。他のほとんどの状況(特にテキストファイルの場合)では、バックグラウンドでバッファリングを処理しながら、簡単かつ柔軟に読み書きするための優れたAPIbufio提供することが非常に便利です。

使用する ioutil

package main

import (
    "io/ioutil"
)

func main() {
    // read the whole file at once
    b, err := ioutil.ReadFile("input.txt")
    if err != nil {
        panic(err)
    }

    // write the whole body at once
    err = ioutil.WriteFile("output.txt", b, 0644)
    if err != nil {
        panic(err)
    }
}

やさしい!ただし、大きなファイルを処理していないことが確実な場合にのみ使用してください。


55
この質問に遭遇した人のために、これらのライブラリが導入される前に2009年に最初に質問されたので、この回答をガイドとして使用してください!
Seth Hoenig 2012年

1
golang.org/pkg/os/#File.Writeによると、書き込みがすべてのバイトを書き込んでいない場合、エラーを返します。したがって、最初の例(panic("error in writing"))の追加のチェックは必要ありません。
2013年

15
これらの例は、fo.Close()から返されたエラーをチェックしていないことに注意してください。Linuxのmanページからclose(2):close()の戻り値をチェックしないことは一般的ですが、それでも重大なプログラミングエラーです。以前のwrite(2)操作のエラーが、最後のclose()で最初に報告される可能性はかなりあります。ファイルを閉じるときに戻り値をチェックしないと、データが失われる可能性があります。これは特にNFSとディスククォータで確認できます。
Nick Craig-Wood

12
では、「大きな」ファイルとは何でしょうか。1KB?1MB?1GB?または、「大きな」はマシンのハードウェアに依存しますか?
425nesp 2014

3
@ 425nespファイル全体をメモリに読み込むため、実行中のマシンで利用可能なメモリの量に依存します。
モスタファ

49

これは良いバージョンです:

package main

import (
  "io/ioutil"; 
  )


func main() {
  contents,_ := ioutil.ReadFile("plikTekstowy.txt")
  println(string(contents))
  ioutil.WriteFile("filename", contents, 0644)
}

8
これにより、ファイル全体がメモリに保存されます。ファイルは大きくなる可能性があるため、常にそうしたいとは限りません。
user7610 2014年

9
また、0x777偽物です。いずれにせよ、それは0644or 0755(16進数ではなく8進数)に近いはずです。
cnst 2014

@cnstが0x777から0644に変更しました
Trenton

31

使用する io.Copy

package main

import (
    "io"
    "log"
    "os"
)

func main () {
    // open files r and w
    r, err := os.Open("input.txt")
    if err != nil {
        panic(err)
    }
    defer r.Close()

    w, err := os.Create("output.txt")
    if err != nil {
        panic(err)
    }
    defer w.Close()

    // do the actual work
    n, err := io.Copy(w, r)
    if err != nil {
        panic(err)
    }
    log.Printf("Copied %v bytes\n", n)
}

ホイールを再発明したくない場合は、io.Copyio.CopyNが役立ちます。io.Copy関数のソース確認すると、Goライブラリにパッケージ化されているMostafaのソリューション(実際には「基本的な」ソリューション)の1つにすぎません。しかし、彼らは彼よりもかなり大きなバッファを使用しています。


5
言及する価値のある1つのこと-ファイルのコンテンツがディスクに書き込まれたことを確認するには、次のw.Sync()後に使用する必要がありますio.Copy(w, r)
Shay Tsadok

また、既存のファイルにio.Copy()書き込む場合は、フィードしたデータのみが書き込まれるため、既存のファイルにさらにコンテンツが含まれている場合は削除されず、ファイルが破損する可能性があります。
Invidian

1
@Invidian宛先ファイルを開く方法によって異なります。その場合はw, err := os.Create("output.txt")、何を説明することは発生しませんので、「作成という名前のファイルを作成するか、切り捨てる。ファイルが既に存在する場合、それが切り捨てられます。」golang.org/pkg/os/#Create
user7610

ファイルを読み取る前に一度にファイル全体を読み取る必要がないため、ホイールを再発明する必要がないため、これは正しい答えです。
Eli Davis

11

新しいGoバージョンでは、ファイルの読み書きは簡単です。ファイルから読み取るには:

package main

import (
    "fmt"
    "io/ioutil"
)

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

ファイルに書き込むには:

package main

import "os"

func main() {
    file, err := os.Create("text.txt")
    if err != nil {
        return
    }
    defer file.Close()

    file.WriteString("test\nhello")
}

これにより、ファイルの内容が上書きされます(ファイルがなかった場合は、新しいファイルを作成します)。


10

[]byteバイト配列のすべてまたは一部の(部分文字列と同様の)スライスです。スライスは、システムが配列(スライス)のすべてまたは一部を見つけてアクセスするための非表示のポインターフィールドと、len()およびcap()関数を使用してアクセスできるスライスの長さと容量のフィールドを持つ値の構造と考えてください。。

バイナリファイルを読み取って出力する、作業用のスターターキットがあります。inNameシステム上の小さなファイルを参照するには、リテラル値を変更する必要があります。

package main
import (
    "fmt";
    "os";
)
func main()
{
    inName := "file-rw.bin";
    inPerm :=  0666;
    inFile, inErr := os.Open(inName, os.O_RDONLY, inPerm);
    if inErr == nil {
        inBufLen := 16;
        inBuf := make([]byte, inBufLen);
        n, inErr := inFile.Read(inBuf);
        for inErr == nil {
            fmt.Println(n, inBuf[0:n]);
            n, inErr = inFile.Read(inBuf);
        }
    }
    inErr = inFile.Close();
}

9
Goの規則では、最初にエラーをチェックし、通常のコードをifブロックの外側に
置き

@Jurily:エラーが発生したときにファイルが開いている場合、どのように閉じますか?
peterSO 2009

10
@peterSO:延期を使用
James

しかし、なぜ[256]バイトが受け入れられず、inBuf:= make([] byte、256)が明らかにばかげて冗長(しかし明らかに間違っていません)が受け入れられるのですか?
カーディフの宇宙飛行士

7

これを試して:

package main

import (
  "io"; 
  )


func main() {
  contents,_ := io.ReadFile("filename");
  println(string(contents));
  io.WriteFile("filename", contents, 0644);
}

1
これは、ファイル全体を一度に読みたい場合に機能します。ファイルが本当に大きい場合、またはファイルの一部のみを読みたい場合は、探しているものではない可能性があります。
エヴァンショー

3
あなたは本当にエラーコードをチェックし、そのようにそれを無視しないでください!!
hasen

7
これはioutilパッケージに移動されました。したがって、ioutil.ReadFile()になります
Christopher

0644と表示されるように修正しました
Joakim

1

ドキュメントを見るだけで、[] byteタイプのバッファを宣言し、それをreadに渡して、それまでに多くの文字を読み取り、実際に読み取った文字数(およびエラー)を返す必要があるようです。

ドキュメントは言う

読み取りは、ファイルから最大len(b)バイトを読み取ります。読み込まれたバイト数と、もしあればエラーを返します。EOFは、errがEOFに設定されたゼロカウントによって通知されます。

それはうまくいきませんか?

編集:また、おそらく、osパッケージを使用する代わりに、bufioパッケージで宣言されたリーダー/ライターインターフェイスを使用する必要があると思います。


あなたが実際に知っている機能のドキュメントを読んだときに、Goに慣れている人たちがREMINDED OF(REMINDED OFを読んでいない)のオウムでなく、実際に人々がドキュメントを読んだときに見るものを実際に認めるので、あなたは私の投票をしました。
カーディフスペースマン

1

Readメソッドは、それが読み込むバッファーであるため、byteパラメーターを取ります。これは一部のサークルでは一般的なイディオムであり、考えてみるとある程度の意味があります。

このようにして、リーダーによって読み取られるバイト数を決定し、戻りを検査して実際に読み取られたバイト数を確認し、エラーを適切に処理できます。

他の人が回答で指摘しているように、bufioはおそらくほとんどのファイルから読み取るためのものです。

本当に便利なので、もう1つヒントを追加します。ファイルから行を読み取るには、ReadLineメソッドではなく、ReadBytesまたはReadStringメソッドを使用するのが最適です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.