ScanFileLines streaming reader with bufio.Scanner
Go
package files
import (
"bufio"
"context"
"fmt"
"os"
)
// ScanFileLines streams path line by line, calling fn with the 1-based line
// number. It never holds the whole file in memory and stops on the first error.
func ScanFileLines(ctx context.Context, path string, fn func(line int, text string) error) error {
f, err := os.Open(path)
if err != nil {
return fmt.Errorf("open %s: %w", path, err)
}
defer f.Close()
sc := bufio.NewScanner(f)
sc.Buffer(make([]byte, 0, 64*1024), 4*1024*1024) // allow long lines
for n := 1; sc.Scan(); n++ {
if err := ctx.Err(); err != nil {
return err
}
if err := fn(n, sc.Text()); err != nil {
return fmt.Errorf("line %d: %w", n, err)
}
}
if err := sc.Err(); err != nil {
return fmt.Errorf("scan %s: %w", path, err)
}
return nil
}