golangファイルを行単位で処理

4441 ワード

golangはpackage bufioを提供しています.bufio.NewReader()はデフォルトサイズのreadbufを作成します.もちろんbufioもできます.NewReaderSize.
func NewReader(rd io.Reader) *Reader
    NewReader returns a new Reader whose buffer has the default size(4096).


func NewReaderSize(rd io.Reader, size int) *Reader
    NewReaderSize returns a new Reader whose buffer has at least the
    specified size. If the argument io.Reader is already a Reader with large
    enough size, it returns the underlying Reader.

bufio
func (b *Reader) ReadByte() (c byte, err error)
    ReadByte reads and returns a single byte. If no byte is available,
    returns an error.

func (b *Reader) ReadBytes(delim byte) (line []byte, err error)
    ReadBytes reads until the first occurrence of delim in the input,
    returning a slice containing the data up to and including the delimiter.
    If ReadBytes encounters an error before finding a delimiter, it returns
    the data read before the error and the error itself (often io.EOF).
    ReadBytes returns err != nil if and only if the returned data does not
    end in delim. For simple uses, a Scanner may be more convenient.

func (b *Reader) ReadString(delim byte) (line string, err error)
    ReadString reads until the first occurrence of delim in the input,
    returning a string containing the data up to and including the
    delimiter. If ReadString encounters an error before finding a delimiter,
    it returns the data read before the error and the error itself (often
    io.EOF). ReadString returns err != nil if and only if the returned data
    does not end in delim. For simple uses, a Scanner may be more
    convenient.

ReadByteというインタフェースは、C言語のfgetcに近く、1バイトずつ読み込まれます.ReadBytesとReadStringはdelimが'′に設定限り、行ごとの読み取りを実現することができる.
package main
import "fmt"
import "os"
import "io"
import "flag"
import "bufio"

var num_flag = flag.Bool("n",false,"num each line")

func usage(){
    fmt.Printf("%s %s
",os.Args[0],"filename") } func cat(r *bufio.Reader){     i := 1     for {         //buf,err := r.ReadBytes('
')         buf,err := r.ReadString('
')         if err == io.EOF{             break         }         if *num_flag{             fmt.Fprintf(os.Stdout,"%5d %s",                         i,buf)             i++         }else{             fmt.Fprintf(os.Stdout,"%s",buf)         }     }     return  } func main(){     flag.Parse()     if(flag.NArg() == 0){         cat(bufio.NewReader(os.Stdin))     }     for i:=0;i<flag.NArg();i++{         f,err := os.OpenFile(flag.Arg(i),os.O_RDONLY,0660)         if err != nil{             fmt.Fprintf(os.Stderr,"%s err read from %s : %s
",             os.Args[0],flag.Arg(0),err)             continue         }         cat(bufio.NewReader(f))         f.Close()     } }

scanerで行単位で読み込む
func cat(scanner *bufio.Scanner) error{

    for scanner.Scan(){
        fmt.Println(scanner.Text())    
      //fmt.Fprintf(os.Stdout,"%s
",scanner.Text())     }     return scanner.Err() }

なぜScanを実行すると、Text()関数は次の行に戻るのでしょうか.デフォルトの分割関数はScanLinesだからです.分割に特別なニーズがある場合は、func(s*Scanner)Split(split SplitFunc)
この関数はSplitFuncを作成することができます.独自の分割関数をカスタマイズできます.
なお、Scanは分割記号を削除し、Fprintfが出力されると、印刷を追加しないで改行しない現象が発生します.以下のようになります
fmt.Fprintf(os.Stdout,"%s",scanner.Text())
manu@manu-hacks:~/code/go/self$ go run mycat_v2.go test.txt 
this is test file created by goif not existed ,please create this fileif existed, Please write appendhello world,hello gothis is test file created by goif not existed ,please create this fileif existed, Please write appendhello world,hello gomanu@manu-hacks:~/code/go/self$ cat test.txt 
this is test file created by go
if not existed ,please create this file
if existed, Please write append
hello world,hello go
this is test file created by go
if not existed ,please create this file
if existed, Please write append
hello world,hello go

呼び出し部分のコードは次のとおりです.
 f,err := os.OpenFile(flag.Arg(i),os.O_RDONLY,0660)
                 ...
        error := cat(bufio.NewScanner(f))
        if err != nil{
            fmt.Fprintf(os.Stderr,"%s err read from %s : %s
",             os.Args[0],flag.Arg(i),error)         }