我是Golang的新手,从一些例子开始。目前,我要做的是逐行读取文件,并在满足特定条件时将其替换为另一个字符串。 该文件用于测试目的包含四行:
one
two
three
four
处理该文件的代码如下所示:
func main() {
file, err := os.OpenFile("test.txt", os.O_RDWR, 0666)
if err != nil {
panic(err)
}
reader := bufio.NewReader(file)
for {
fmt.Print("Try to read ...\n")
pos,_ := file.Seek(0, 1)
log.Printf("Position in file is: %d", pos)
bytes, _, _ := reader.ReadLine()
if (len(bytes) == 0) {
break
}
lineString := string(bytes)
if(lineString == "two") {
file.Seek(int64(-(len(lineString))), 1)
file.WriteString("This is a test.")
}
fmt.Printf(lineString + "\n")
}
file.Close()
}
正如您在代码片段中看到的那样,只要从文件中读取此字符串,我就要将字符串“two”替换为“This is a test”。 为了获取文件中的当前位置,我使用Go的 Seek 方法。 然而,发生的事情是总是最后一行被替换。这是一个测试,使文件看起来像这样:
one
two
three
This is a test
检查将当前文件位置写入终端的print语句的输出,在读取第一行后得到那种输出:
2016/12/28 21:10:31 Try to read ...
2016/12/28 21:10:31 Position in file is: 19
所以在第一次读取之后,位置光标已经指向我文件的末尾,这解释了为什么新字符串被附加到结尾。有谁知道这里发生了什么,或者更确切地说是导致这种行为的原因是什么?
答案 0 :(得分:3)
Reader不是file.Seek
的控制器。您已将读者声明为:reader := bufio.NewReader(file)
然后您一次只读一行bytes, _, _ := reader.ReadLine()
但file.Seek
并未改变读者正在阅读的位置。
建议您阅读文档中的ReadSeeker并切换到使用该文档。还有一个使用SectionReader的例子。
答案 1 :(得分:0)
除了不正确的查找用法外,困难在于您要替换的行与替换的行长不同。标准方法是使用所做的修改创建一个新的(临时)文件。假设成功,则用新文件替换原始文件。
package main
import (
"bufio"
"io"
"io/ioutil"
"log"
"os"
)
func main() {
// file we're modifying
name := "text.txt"
// open original file
f, err := os.Open(name)
if err != nil {
log.Fatal(err)
}
defer f.Close()
// create temp file
tmp, err := ioutil.TempFile("", "replace-*")
if err != nil {
log.Fatal(err)
}
defer tmp.Close()
// replace while copying from f to tmp
if err := replace(f, tmp); err != nil {
log.Fatal(err)
}
// make sure the tmp file was successfully written to
if err := tmp.Close(); err != nil {
log.Fatal(err)
}
// close the file we're reading from
if err := f.Close(); err != nil {
log.Fatal(err)
}
// overwrite the original file with the temp file
if err := os.Rename(tmp.Name(), name); err != nil {
log.Fatal(err)
}
}
func replace(r io.Reader, w io.Writer) error {
// use scanner to read line by line
sc := bufio.NewScanner(r)
for sc.Scan() {
line := sc.Text()
if line == "two" {
line = "This is a test."
}
if _, err := io.WriteString(w, line+"\n"); err != nil {
return err
}
}
return sc.Err()
}
对于更复杂的替换,我实现了一个可以替换正则表达式匹配项的程序包。 https://github.com/icholy/replace
import (
"io"
"regexp"
"github.com/icholy/replace"
"golang.org/x/text/transform"
)
func replace2(r io.Reader, w io.Writer) error {
// compile multi-line regular expression
re := regexp.MustCompile(`(?m)^two$`)
// create replace transformer
tr := replace.RegexpString(re, "This is a test.")
// copy while transforming
_, err := io.Copy(w, transform.NewReader(r, tr))
return err
}