> For the complete documentation index, see [llms.txt](https://wskdsgcf.gitbook.io/mastering-go-zh-cn/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://wskdsgcf.gitbook.io/mastering-go-zh-cn/08.0/08.5/08.5.1.md).

# 08.5.1 逐行读取文本文件

逐行读取文本文件是最常用的方式。这也是我们首先介绍它的原因。`byLine.go`程序分为三部分，将帮助你理解这个技巧。

`byLine.go`第一部分代码如下：

```go
package main

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

引入`bufio`包表示我们将使用缓冲区输入。

```go
func lineByLine(file string) error {
	var err error

	f, err := os.Open(file)
	if err != nil {
		return err
	}
	defer f.Close()

	r := bufio.NewReader(f)
	for {
		line, err := r.ReadString('\n')
		if err == io.EOF {
			break
		} else if err != nil {
			fmt.Printf("error reading file %s", err)
			break
		}
		fmt.Printf(line)
	}
	return nil
}
```

所有的实现都在`lineByLine()`函数中。在确保可以打开指定的文件名进行读取之后，你调用`bufio.NewReader()`创建一个新的读实例，然后你可以调用`bufio.ReadString()`逐行读取文件。行分隔符通过`bufio.ReadString()`参数指定，它指示`bufio.ReadString()`一直读取，直到碰到行分隔符为止。当参数是换行符时，不断调用`bufio.ReadString()`会逐行读取输入文件！注意，使用`fmt.Print()`而不是`fmt.Println()`输出读取行，说明每个输入行中都包含了换行符。

`byLine.go`第三部分代码如下：

```go
func main() {
	flag.Parse()
	if len(flag.Args()) == 0 {
		fmt.Printf("usage: byLine <file1> [<file2> ...]\n")
		return
	}

	for _, file := range flag.Args() {
		err := lineByLine(file)
		if err != nil {
			fmt.Println(err)
		}
	}
}
```

执行`byLine.go`，并使用`wc(1)`处理输出会产生如下的输出内容：

```shell
$ go run byLine.go /tmp/swtag.log /tmp/adobegc.log | wc
        4761   88521   568402 
```

如下的命令会校验前述输出的精确性：

```shell
$ wc /tmp/swtag.log /tmp/adobegc.log
         131     693     8440     /tmp/swtag.log
        4630   87828   559962     /tmp/adobegc.log
        4761   88521   568402     total
```
