標準愚痴出力

個人的なIT作業ログです。もしかしたら一般的に参考になることが書いているかもしれません(弱気

rangefunc の練習:Yet Another Scanner

  • 既存のライブラリに bufio.Scanner があるが、改行をカットしてしまう
  • 改行を含めて読み取りたいときは (*bufio.Reader) ReadString('\n') を使う
    • EOF を検出した時の処理が面倒だが、雑に書くと最終行が失われてしまう

面倒なく、ありのままに読み込むコードを楽に書くための iterator を書いた

foo.go

package main

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

func readlines(r io.Reader) func(func(string, error) bool) {
    return func(yield func(string, error) bool) {
        var br *bufio.Reader
        if _br, ok := r.(*bufio.Reader); ok {
            br = _br
        } else {
            br = bufio.NewReader(r)
        }
        for {
            line, err := br.ReadString('\n')
            if err != nil && err != io.EOF {
                yield(line, err)
                return
            }
            if !yield(line, nil) {
                return
            }
            if err == io.EOF {
                return
            }
        }
    }
}
// 以上がライブラリコード
// 以下、使用例

func mains() error {
    for line, err := range readlines(os.Stdin) {
        if err != nil {
            return err
        }
        fmt.Print(line)
    }
    return nil
}

func main() {
    if err := mains(); err != nil {
        fmt.Fprintln(os.Stderr, err.Error())
        os.Exit(1)
    }
}

サンプルファイル foo.txt (制御コードが分かるよう、バイナリエディターのアウトプットで)

$ binview.exe  foo.txt
binview snapshot-windows-amd64 by go1.17.7
00000000 41 42 43 0D 0A 44 45 46 0D 0A 47 48 49          ABC←↓DEF←↓GHI
 [UTF8]  70='\x46'(1/1:U+0046) @ 7=0x7/13=0xD

実行結果:

[1] <DESKTOP-NPOTG52:~>
$ env GOEXPERIMENT=rangefunc go run foo.go < foo.txt
ABC
DEF
GHI[1] <DESKTOP-NPOTG52:~>
$