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

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

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

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

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

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

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

```go
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
コメントいただいたので修正版．

```go
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
サンプルを参考に，上の例と同じにするとこんな感じでしょうか．

```go
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`に設定するのがなんか気持ち悪いですが・・・．
下記はタブ区切りのファイルを読む場合のサンプルです．

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