用Golang替换文件中的一行

时间:2016-12-28 20:16:24

标签: go io

我是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

所以在第一次读取之后,位置光标已经指向我文件的末尾,这解释了为什么新字符串被附加到结尾。有谁知道这里发生了什么,或者更确切地说是导致这种行为的原因是什么?

2 个答案:

答案 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
}