读文件
代码实例
// 读写文件在很多程序中都是必须的基本任务。首先我们看看一
// 些读文件的例子。
package main
import (
"bufio"
"fmt"
"io"
"io/ioutil"
"os"
)
// 读取文件需要经常进行错误检查,这个帮助方法可以精简下面
// 的错误检查过程。
func check(e error) {
if e != nil {
panic(e)
}
}
func main() {
// 也许大部分基本的文件读取任务是将文件内容读取到
// 内存中。
dat, err := ioutil.ReadFile("/tmp/dat")
check(err)
fmt.Print(string(dat))
// 你经常会想对于一个文件是怎么读并且读取到哪一部分
// 进行更多的控制。对于这个任务,从使用 `os.Open`
// 打开一个文件获取一个 `os.File` 值开始。
f, err := os.Open("/tmp/dat")
check(err)
// 从文件开始位置读取一些字节。这里最多读取 5 个字
// 节,并且这也是我们实际读取的字节数。
b1 := make([]byte, 5)
n1, err := f.Read(b1)
check(err)
fmt.Printf("%d bytes: %s\n", n1, string(b1))
// 你也可以 `Seek` 到一个文件中已知的位置并从这个位置开
// 始进行读取。
o2, err := f.Seek(6, 0)
check(err)
b2 := make([]byte, 2)
n2, err := f.Read(b2)
check(err)
fmt.Printf("%d bytes @ %d: %s\n", n2, o2, string(b2))
// `io` 包提供了一些可以帮助我们进行文件读取的函数。
// 例如,上面的读取可以使用 `ReadAtLeast` 得到一个更
// 健壮的实现。
o3, err := f.Seek(6, 0)
check(err)
b3 := make([]byte, 2)
n3, err := io.ReadAtLeast(f, b3, 2)
check(err)
fmt.Printf("%d bytes @ %d: %s\n", n3, o3, string(b3))
// 没有内置的回转支持,但是使用 `Seek(0, 0)` 实现。
_, err = f.Seek(0, 0)
check(err)
// `bufio` 包实现了带缓冲的读取,这不仅对有很多小的读
// 取操作的能提升性能,也提供了很多附加的读取函数。
r4 := bufio.NewReader(f)
b4, err := r4.Peek(5)
check(err)
fmt.Printf("5 bytes: %s\n", string(b4))
// 任务结束后要关闭这个文件(通常这个操作应该在 `Open`
// 操作后立即使用 `defer` 来完成)。
f.Close()
}
运行程序
$ echo "hello" > /tmp/dat
$ echo "go" >> /tmp/dat
$ go run reading-files.go
hello
go
5 bytes: hello
2 bytes @ 6: go
2 bytes @ 6: go
5 bytes: hello
# 下面我们将看一下写入文件。
写文件
代码实例
// Go 写文件和我们前面看过的读操作有着相似的方式。
package main
import (
"bufio"
"fmt"
"io/ioutil"
"os"
)
func check(e error) {
if e != nil {
panic(e)
}
}
func main() {
// 开始,这里是展示如写入一个字符串(或者只是一些
// 字节)到一个文件。
d1 := []byte("hello\ngo\n")
err := ioutil.WriteFile("/tmp/dat1", d1, 0644)
check(err)
// 对于更细粒度的写入,先打开一个文件。
f, err := os.Create("/tmp/dat2")
check(err)
// 打开文件后,习惯立即使用 defer 调用文件的 `Close`
// 操作。
defer f.Close()
// 你可以写入你想写入的字节切片
d2 := []byte{115, 111, 109, 101, 10}
n2, err := f.Write(d2)
check(err)
fmt.Printf("wrote %d bytes\n", n2)
// `WriteString` 也是可用的。
n3, err := f.WriteString("writes\n")
fmt.Printf("wrote %d bytes\n", n3)
// 调用 `Sync` 来将缓冲区的信息写入磁盘。
f.Sync()
// `bufio` 提供了和我们前面看到的带缓冲的读取器一
// 样的带缓冲的写入器。
w := bufio.NewWriter(f)
n4, err := w.WriteString("buffered\n")
fmt.Printf("wrote %d bytes\n", n4)
// 使用 `Flush` 来确保所有缓存的操作已写入底层写入器。
w.Flush()
}
运行程序
# 运行这端文件写入代码。
$ go run writing-files.go
wrote 5 bytes
wrote 7 bytes
wrote 9 bytes
# 然后检查写入文件的内容。
$ cat /tmp/dat1
hello
go
$ cat /tmp/dat2
some
writes
buffered
# 下面我们将看一些文件 I/O 的想法,就像我们已经看过的
# `stdin` 和 `stdout` 流。
行过滤器
代码实例
// 一个_行过滤器_ 在读取标准输入流的输入,处理该输入,然后
// 将得到一些的结果输出到标准输出的程序中是常见的一个功能。
// `grep` 和 `sed` 是常见的行过滤器。
// 这里是一个使用 Go 编写的行过滤器示例,它将所有的输入文字
// 转化为大写的版本。你可以使用这个模式来写一个你自己的 Go
// 行过滤器。
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
// 对 `os.Stdin` 使用一个带缓冲的 scanner,让我们可以
// 直接使用方便的 `Scan` 方法来直接读取一行,每次调用
// 该方法可以让 scanner 读取下一行。
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
// `Text` 返回当前的 token,现在是输入的下一行。
ucl := strings.ToUpper(scanner.Text())
// 写出大写的行。
fmt.Println(ucl)
}
// 检查 `Scan` 的错误。文件结束符是可以接受的,并且
// 不会被 `Scan` 当作一个错误。
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)
}
}
运行程序
# 试一下我们的行过滤器,首先创建多一个有小写行的文件。
$ echo 'hello' > /tmp/lines
$ echo 'filter' >> /tmp/lines
# 然后使用行过滤器来得到大些的行。
$ cat /tmp/lines | go run line-filters.go
HELLO
FILTER