Go でファイルを1行ずつ読み込む(csv ファイルも)

  • 102
    いいね
  • 5
    コメント

概要

ファイルを読み込んで処理することがよくあるので忘備用のメモです.

ファイルを1行ずつ読み込む

bufio.Readerで読み込む

ReadString

引数にファイルが指定されていれば,ファイルをオープンして1行ずつ読み込みます.
引数が指定されていなければ,標準入力から1行ずつ読み込みます.

追記:ReadString('\n')で読み込むとWindowsの場合\rが残ってしまうのでReadLineの方がいいとのコメントいただきましたので,ReadLine使った版も載せておきました.

os.EOFでなくてio.EOFなのがはまりどころでした.

Readerはファイルをクローズすれば閉じられるみたいです.

package main

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

func main() {
        var fp *os.File
        var err error

        if len(os.Args) < 2 {
                fp = os.Stdin
        } else {
                fmt.Printf(">> read file: %s\n", os.Args[1])
                fp, err = os.Open(os.Args[1])
                if err != nil {
                        panic(err)
                }
                defer fp.Close()
        }

        reader := bufio.NewReaderSize(fp, 4096)
        for line := ""; err == nil; line, err = reader.ReadString('\n') {
                fmt.Print(line)
        }
        if err != io.EOF {
                panic(err)
        }
}

ReadLine

コメントいただいたので修正版.

package main

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

func main() {
        var fp *os.File
        var err error

        if len(os.Args) < 2 {
                fp = os.Stdin
        } else {
                fmt.Printf(">> read file: %s\n", os.Args[1])
                fp, err = os.Open(os.Args[1])
                if err != nil {
                        panic(err)
                }
                defer fp.Close()
        }

        reader := bufio.NewReaderSize(fp, 4096)
        for {
                line, _, err := reader.ReadLine()
                fmt.Println(string(line))
                if err == io.EOF {
                        break
                } else if err != nil {
                        panic(err)
                }
        }
}

bufio.Scannerで読み込む (推奨)

面倒だなとおもったらbufioにScanner という便利なものがありました.
http://golang.org/pkg/bufio/#example_Scanner_lines
サンプルを参考に,上の例と同じにするとこんな感じでしょうか.

package main

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

func main() {
        var fp *os.File
        var err error

        if len(os.Args) < 2 {
                fp  = os.Stdin
        } else {
                fp, err = os.Open(os.Args[1])
                if err != nil {
                   panic(err)
                }
                defer fp.Close()
        }

        scanner := bufio.NewScanner(fp)
        for scanner.Scan() {
            fmt.Println(scanner.Text())
        }
        if err := scanner.Err(); err != nil {
            panic(err)
        }
}

csv(tsv)形式のファイルの読み込み

csv形式のファイルを読み込むReaderが用意されているのでこれを使えば簡単.
区切り文字の指定をReader.Commaに設定するのがなんか気持ち悪いですが・・・.
下記はタブ区切りのファイルを読む場合のサンプルです.

package main

import (
        "encoding/csv"
        "fmt"
        "io"
        "os"
)

func main() {

        var fp *os.File
        if len(os.Args) < 2 {
                fp = os.Stdin
        } else {
                var err error
                fp, err = os.Open(os.Args[1])
                if err != nil {
                        panic(err)
                }
                defer fp.Close()
        }

        reader := csv.NewReader(fp)
        reader.Comma = '\t'
        reader.LazyQuotes = true // ダブルクオートを厳密にチェックしない!
        for {
                record, err := reader.Read()
                if err == io.EOF {
                        break
                } else if err != nil {
                        panic(err)
                }
                fmt.Println(record)
        }
}