csvを行ごとに処理する(GO言語)

下のコードをインポートして、
package csvparserow

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

//iにforのループ数,cnumに列数
type Csvrowdeal func(row []string, icnt int, cnum int)

//戻り値には最後まで読んだ行数が入る。
func Csvparse(rowF Csvrowdeal, csvr io.Reader, dedelimiter rune) (int, int) {

    var err error
    //ひとつの行のカラムの長さも考える必要があるかも。
    var row []string

    //csvのデリ見たが指定できていないのであとでしてい。
    //csvを読み込むために必要
    cin := csv.NewReader(csvr)

    //CSV.Commaもデリミタは1バイトで決め打ちしている。ためこれでよい。
    //string[0]で勝手にbyteにキャストされるので後で変換する必要がある。
    //runeで入れる必要がある。runeもstringもバイト列のためこのようなことができる。
    cin.Comma = dedelimiter

    //forの三項目目はfor文の最初に評価されるのでここでも一度読んでおく。
    row, err = cin.Read()

    i := 0
    var cnum int
    //ストリームの最後まで読んだら、ENDになる。
    for ; err != io.EOF; row, err = cin.Read() {

        if i == 0 {
            cnum = len(row)
        }
        //EOF意外のエラーだった場合は標準エラー出力に吐き出す。
        if err != io.EOF && err != nil {
            fmt.Fprintln(os.Stderr, "CSV形式のデータを正しく読み取れませんでした。")
            os.Exit(1)

        }

        //行ごとの処理を呼び出す関数側で設定する。
//下の呼び出す側の関数
        rowF(row, i, cnum)

        i++
    }

    return i, cnum

}



使う時は、上で定義したcsvrowdeal型の匿名関数を
引数に渡して実行するとよい。
下の赤字の部分が実際にパースされる部分
package main //cut -f -d
import (
    "flag"
    "fmt"
    "io"
    "io/ioutil"
    "os"
    "sort"
    "strconv"
    "strings"

    "github.com/KatsutoshiOtogawa/csvparserow"
)

var (
    cformat = flag.String("f", "", "help message for f option")
    dedelimiter = flag.String("d", ",", "help message for d option")
)

//前から何列削除というふうにも作るべき。fflag
//""で文字列をちゃんと区切らないとエラーになる。
func main() {

    //これがないとオプション引数が展開されないので注意。
    flag.Parse()

    if (*cformat) == "" {
        fmt.Fprintln(os.Stderr, "-fオプションは必須です。")

        //golangはioutil.Discardを使って捨てることになる。
        io.Copy(ioutil.Discard, os.Stdin)

        os.Exit(1)
    }
    var err error
    cnum := strings.Split((*cformat), ",")

    var col []int
    var cols []string
    col = make([]int, len(cnum))

    cols = make([]string, len(cnum))

    icnt := 0
    var val int
    //ループを使って文字列の数字をintの数字に変更して代入し直している。
    for range cnum {
        val, err = strconv.Atoi(cnum[icnt])

        if err != nil && val > 0 {
            fmt.Fprintln(os.Stderr, "-fオプションには自然数を洗濯してください。")

            //golangはioutil.Discardを使って捨てることになる。
            io.Copy(ioutil.Discard, os.Stdin)

            os.Exit(1)
        }

        //csvの列は0から始まるので一つずれる。
        col[icnt] = val - 1
        icnt++
    }

    //Unixコマンドのcut -f 1,3などの指定も順不同のため
    //動作を統一させるためにソートを使う。
    sort.Ints(col)

    //csvをパースするのに使う区切り文字です。
    de := rune((*dedelimiter)[0])
    //csvをパースした後にもう一度csvに戻すときの区切り文字が入ります。
    en := de


  //!!!ここに実際パースされる部分が描かれる。渡される匿名関数が行ごとの処理
    _, _ = csvparserow.Csvparse(func(row []string, i int, columnnum int) {

        //ループを使ってコマンドから、指定した数字からカラムを指定している。
        for j := 0; j < len(cnum); j++ {
            cols[j] = row[col[j]]
        }

        fmt.Println(strings.Join(cols, string(en)))

    }, os.Stdin, de)

}

この関数はこれから作成するcsv操作ようのライブラリにも使う。



コメント

人気の投稿