定型データの入力

定型のデータを読み込んで、型変換をしてくれる関数があると便利だ。
fmt パッケージの ScanFscan は空白文字で区切られたテキストデータを読み込み、可変長引数で渡されたデータ形式の変数(のポインタ)に変換して格納してくれる。Scan は標準入力から、Fscanio.Reader から読み込む。
まずは Scan の例。

package main

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

func main() {
    sumi := 0
    sumf := 0.0
    suma := make([]string, 0)

    for {
        var n int
        var m float64
        var s string
        i, err := fmt.Scan(&n, &m, &s)
        if i == 3 {
            sumi += n
            sumf += m
            suma = append(suma, s)
        } else if i == 0 && err == io.EOF {
            break
        } else {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }
    }
    fmt.Println(sumi, sumf, suma)
}
^o^ > go run scan.go
1 1.5 1
2 2.3 2
3 3.8 3
6 7.6 [1 2 3]

go run scan.go コマンドに続く3行が入力で、1行ごとに整数、実数、文字列に変換される。ctrl + c で入力を終了すると、結果が表示される。最後の行が結果だ。

次は Fscan の例。io.Reader から(つまりファイルから)読み込む。

package main

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

func main() {
    sumi := 0
    sumf := 0.0
    suma := make([]string, 0)

    filename := os.Args[1]
    file, err := os.Open(filename)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    for {
        var n int
        var m float64
        var s string
        i, err := fmt.Fscan(file, &n, &m, &s)
        if i == 3 {
            sumi += n
            sumf += m
            suma = append(suma, s)
        } else if i == 0 && err == io.EOF {
            break
        } else {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }
    }
    file.Close()
    fmt.Println(sumi, sumf, suma)
}
^o^ > cat fscan_sample.txt
10 1.234 foo
20 5.678 bar
30 9.876 baz

^o^ > go run fscan.go fscan_sample.txt
60 16.788 [foo bar baz]

ScanfFscanf は書式付き入力だ。引数で指定された書式で入力を解釈して読み込んでくれる。Scanf は標準入力から、Fscanfio.Reader (つまりファイル)から読み込む。
Scanf の例。

package main

import (
    "fmt"
    "os"
)

func main() {
    sumi := 0
    sumf := 0.0
    suma := make([]string, 0)
    for {
        var n int
        var m float64
        var s string
        i, err := fmt.Scanf("%d,%f,%q\n", &n, &m, &s)
        if i == 3 {
            sumi += n
            sumf += m
            suma = append(suma, s)
        } else if i == 0 {
            break
        } else {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }
    }
    fmt.Println(sumi, sumf, suma)
}

この例では整数、実数、クォートされた文字列をカンマ区切りしたものを入力として期待している。

^o^ > go run scanf.go
10,1.234,"foo"
20,5.678,"bar"
30,9.876,"baz"
60 16.788 [foo bar baz]

最後に Fscanf。これはファイルから読み込むことのほかは Scanf と同じだ。

package main

import (
    "fmt"
    "os"
)

func main() {
    sumi := 0
    sumf := 0.0
    suma := make([]string, 0)

    filename := os.Args[1]
    file, err := os.Open(filename)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    for {
        var n int
        var m float64
        var s string
        i, err := fmt.Fscanf(file, "%d,%f,%q\n", &n, &m, &s)
        if i == 3 {
            sumi += n
            sumf += m
            suma = append(suma, s)
        } else if i == 0 {
            break
        } else {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }
    }
    file.Close()
    fmt.Println(sumi, sumf, suma)
}
^o^ > cat fscanf_sample.txt
10,1.234,"foo"
20,5.678,"bar"
30,9.876,"baz"

^o^ > go run fscanf.go fscanf_sample.txt
60 16.788 [foo bar baz]