Golang:文件写
#go #100daysofgolang

介绍

在该系列的第24篇文章中,我们将研究如何使用Golang对文件进行写操作。我们将在大多数操作中使用os软件包以及bufio文本操作。我们将执行写入操作,例如使用Golang附加,删除和替换文件。我们将大力利用标准库包,例如osbufiobytesfmt。我们还将研究覆盖和字符串格式为文件。

写入文件

本节的第一部分是文件的基本写操作,我们假设我们正在写入新文件并覆盖现有文件的内容。下一部分将介绍内容上的内容,依此类推。在此示例中,我们将看到如何执行基本写作操作以编写字符串,将字符串切成片段。

package main

import (
    "log"
    "os"
)

func HandleError(err){
    if err != nil {
        log.Fatal(err)
    }
}

func main() {
    str := "Hi, I am a gopher!\n"
    f, err := os.OpenFile("abc.txt", os.O_WRONLY, 0660)
    // f, err := os.Create("abc.txt")
    HandleError(err)
    _, err = f.Write([]byte(str))
    HandleError(err)
    defer f.Close()
}
$ cat abc.txt

$ go run main.go

$ cat abc.txt
Hi, I am a gopher!

因此,我们使用了一个简单的Golang脚本来写入已经创建/已经创建的文件。如果您不需要在不存在的文件上写入错误,请使用Create方法,而不是类似于Open方法,但如果不存在,则创建文件。我们使用Write方法将内容覆盖到文件中,它将参数作为字节片,因此我们使用[]byte(str)语法将字符串str键入[]byte。因此,我们将字符串的内容写入文件中。我们使用延期关键字在脚本末尾或主函数范围的末尾关闭文件。

写一块字符串以归档

我们甚至可以使用for loop编写字符串切片以文件并用新的行字符附加每个字符串。

package main

import (
    "log"
    "os"
)

func HandleError(err){
    if err != nil {
        log.Fatal(err)
    }
}

func main() {
    f, err := os.Create("abc.txt")
    //f, err := os.Open("abc.txt", os.O_WRONLY, 0660)
    langs := []string{"golang", "python", "rust", "javascript", "ruby"}
    for _, lang := range langs {
        _, err := f.WriteString(lang + "\n")
        HandleError(err)
    }
    defer f.Close()
}
$ cat abc.txt

$ go run main.go

$ cat abc.txt
golang
python
rust
javascript
ruby

我们使用了WriteString方法,该方法将以字符串作为参数而不是字节片。因此,我们不必将铸件键入字节片。因此,正如我们所看到的那样,我们已经将字符串切片写入文件中。

过度写

写入文件的最小代码是os软件包中的WriteFile函数,它用提供的字节的片段,文件名和必要的书面许可覆盖文件的内容。如果不存在Funciton,则将创建文件,这是错误的原因之一。尽管它返回错误对象,但由于不正确写入文件,编码问题等,可能会创建错误。

package main

import (
    "log"
    "os"
)

func main() {
    data := []byte{115, 111, 109, 101, 65}
    err := os.WriteFile("test.txt", data, 0660)
    log.Println(err)

    s := "Hello"
    err = os.WriteFile("test.txt", []byte(s), 0660)
    log.Println(err)
}
$ go run main.go
2022/12/17 19:24:13 <nil>
2022/12/17 19:24:13 <nil>

$ cat test.txt
Hello

因此,我们在脚本中使用了两次WriteFile方法,它首先采用了一块字节,因为它被定义为data,它对应于115 -> s111 -> o65 -> A,ASCII14,ASCII映射到字符串。字节的切片可以作为像someA这样的字符串作为字节基础切片的字面价值。因此,我们将那个字节切成片并解析为WriteFile函数的第二个参数。第一个参数是我们要写入内容的文件的字符串路径,第三个参数是文件权限。我们将其设置为0660,指示向组和用户读取(4) +写入(2),并且对其他用户没有许可。该函数将返回错误(如果有),否则它将仅覆盖文件中的数据。

在这种情况下,我们将带有字符串s类型的WriteFile方法称为脚本末尾的字节切片,因此我们看到该文件的内容为Hello而不是someA。如果我们扭转了操作,我们将不会在文件中看到Hello字符串。

package main

import (
    "log"
    "os"
)

func main() {
    s := "Hello"
    err := os.WriteFile("test.txt", []byte(s), 0660)
    log.Println(err)

    data := []byte{115, 111, 109, 101, 65}
    err = os.WriteFile("test.txt", data, 0660)
    log.Println(err)
}
$ go run main.go
2022/12/17 19:24:13 <nil>
2022/12/17 19:24:13 <nil>

$ cat test.txt
someA

我们可以看到,Hello已被someA覆盖。

写格式的字符串

我们甚至可以使用FMT将格式的字符串写入文件。就像我们可以使用Scanf获取输入一样,我们可以使用Fprint和其他类似功能(例如Fprintf)和Fprintln函数来打印/添加内容。

package main

import (
    "fmt"
    "log"
    "os"
)

func HandleErr(err error) {
    if err != nil {
        log.Fatal(err)
    }
}
func main() {
    f, err := os.Create("temp.txt")
    HandleErr(err)
    name, lang, exp := "John", "go", 2
    _, err = fmt.Fprint(f, "Hi, I am ", name, "\n")
    HandleErr(err)
    _, err = fmt.Fprintf(f, "Language of choice: %s.\n", lang)
    HandleErr(err)
    _, err = fmt.Fprintln(f, "Having", exp, "years of experience.")
    HandleErr(err)
    defer f.Close()
}
$ cat temp.txt
cat: temp.txt: No such file or directory

$ go run format.go

$ cat test.txt
Hi, I am John.
Language of choice: go.
Having 2 years of experience.

因此,我们可以看到我们已经使用了所有具有自己用例的方法,我们可以使用Fprint进行简单字符串,Fprintf用于用多占位持有人格式化字符串的块,而Fprintln则像Fprint一样工作,但是它添加了一个新行本身,我们不需要明确指定。

附加

如果我们想将文本附加到文件,我们可以使用OpenFile函数并提供一些参数来附加内容而不是覆盖。

在这里,我们有两个步骤,打开文件,然后在文件中写入内容。因此,在打开文件时,我们提供了一些选项作为参数,以使调整的系统调用像仅打开读,写或附加模式一样。这些选项定义为os package中的常量INT值。

package main

import (
    "log"
    "os"
)

func HandleError(err error) {
    if err != nil {
        log.Fatal(err)
    }
}

func main() {
    s := "Hello"
    err := os.WriteFile("test.txt", []byte(s), 0660)
    HandleError(err)

    s = "World"
    f, err := os.OpenFile("test.txt", os.O_APPEND|os.O_WRONLY, 0660)
    HandleError(err)
    _, err = f.WriteString(s)
    HandleError(err)
    defer f.Close()
}
$ go run main.go

$ cat test.txt
HelloWorld

因此,从上面的示例中,我们能够将文本附加到文件中,我们首先使用WriteFile方法将Hello World String添加到文件中,以指示我们覆盖文件的先前内容。然后,我们使用OpenFile方法来打开第一个参数中提供的文件作为字符串路径。第二个参数是要传递以在打开文件上执行操作的选项,我们始终必须使用它们defer来关闭文件或其他资源锁定操作。

我们已经指定了os.O_WRONLYos.O_APPEND选项,指示我们要在打开文件并专门将文件附加到文件时将其写入文件。因此,这是对打开的文件操作进行微调。我们可以使用刚刚用于简单读取和写入操作的ReadFile或WriteFile操作。

我们使用WriteString方法,但是我们甚至可以使用Write方法来编写字节片。这只是用于探索OS软件包的文件类型中的不同选项。

在特定行上附加

我们还可以将内容添加到文件的特定行或部分。 Golang没有直接的功能要执行此操作,我们将必须对文件操作进行一些手动微调以在特定行中附加特定文本。

package main

import (
    "bufio"
    "bytes"
    "log"
    "os"
)

func HandleError(err error) {
    if err != nil {
        log.Fatal(err)
    }
}

func main() {
    /* test.txt
    Hi
    Hello
    World
    Gopher
    */
    f, err = os.OpenFile("test.txt", os.O_RDWR, 0660)
    defer f.Close()
    HandleError(err)
    m := bufio.NewScanner(f)
    bytes_till := 0
    // line to be appended
    line_till := 2
    var lines_after string
    var lines_till string
    i := 0
    for m.Scan() {
        line := m.Text()
        if i < line_till {
            bytes_till += bytes.Count([]byte(line), []byte{})
            if i > 0 {
                lines_till += "\n"
            }
            lines_till += line
        } else {
            lines_after += "\n" + line
        }
        i += 1
    }
    HandleError(m.Err())
    insert_text := lines_till + "\nInserted content"
    insert_text_bytes := bytes.Count([]byte(insert_text), []byte{})
    lines_after_bytes := bytes.Count([]byte(lines_after), []byte{})

    err = os.WriteFile("test.txt", []byte(insert_text), 0660)
    HandleError(err)
    _, err = f.WriteAt([]byte(lines_after), int64(lines_after_bytes)+int64(insert_text_bytes))
    HandleError(err)
    /* test.txt
    Hi
    Hello
    Inserted content
    World
    Gopher
    */
}
$ cat test.txt 
Hi
Hello
World
Gophers

$ go run append.go 

$ cat test.txt 
Hi
Hello
Inserted content
World
Gophers

我们在第二行之后插入了Inserted content,因为line_till变量设置为2

因此,在上面的示例中,我们使用了一堆软件包将字符串或任何形式的文本附加到特定行中。我们首先使用OpenFile方法读取文件的内容,该方法将在某些权限中打开文件。我们需要在脚本末尾关闭文件,因此我们只需在f.Close()方法调用之前使用defer关键字即可。然后,我们通过使用NewScanner方法创建Scanner对象来扫描文件缓冲区。然后,使用文件内容的扫描仪对象,我们可以使用Scan()方法逐行扫描文件目录。通过使用Text将每行的内容从一个字节片转换为字符串,我们将其附加到字符串line,这将用于保留在新插入的文本之前的字节计数,用于附加文本。

>

line_till变量用于我们要在该线路上附加到文本的行号。

我们对当前行的字节进行计数,并将其添加到bytes_till变量中,该变量指示在附加内容之前的字节数。我们有一个简单的if-else检查,以添加新的字符线。我们将行附加到一个字符串lines_till中。字符串insert_text是通过在行号line_till之前加上要插入实际内容的所有行来创建的。我们使用字节软件包中的Count方法计算字节数。分离器保持空白。 lines_after也是在文件中的行号之后创建的。

我们使用WriteFileinsert_text( +插入文本之前的行)添加到文件中,该WriteFile将覆盖文件的内容。然后,我们将lines_after字符串作为字节片附加到insert_text_bytes + lines_after_bytes,因此我们获得了字节号位置以附加lines_after字符串。

简而言之,我们基本上是通过创建两个字符串(slice oftes)来覆盖文件,该字符串在线号之前,带有文本,第二个字符串在行号之后具有所有行。

替换文件中的文本

使用bytes.Replace方法,我们可以首先读取所有字节并将旧字节替换为新文本,然后将其存储为一个字节片。然后,我们再次将这些字节的切片编写到文件中,因此我们首先将内容读取为字节的切片,替换字节的内容,然后用字节的切片覆盖内容。这很简单。

package main

import (
    "bytes"
    "log"
    "os"
)

func HandleError(err error) {
    if err != nil {
        log.Fatal(err)
    }
}

func main() {
    filename := "test.txt"
    file, err := os.ReadFile(filename)
    HandleError(err)
    old_text := "Hello\nWorld"
    new_text := "Bye"
    new_content := bytes.Replace(file, []byte(old_text), []byte(new_text), -1)
    err = os.WriteFile(filename, new_content, 0660)
    HandleError(err)
}
$ cat test.txt
Hi
Hello
World
Gophers

$ go run main.go


$ cat test.txt
Hi
Bye
Gophers

正如我们可以看到的那样,我们已用Bye替换了Hello\nWorld。因此,字节软件包中的[替换]方法采用的参数(例如字节的切片)应该是文件的实际内容,旧文本要再次替换为字节片,而新文本则替换同样,作为字节的切片,最终参数是要替换的数量。这里的-1表示可以进行多少替换,可以是12用于替换旧文本的第一个n出现,具体取决于您想替换文件中的内容的次数。

从文件中删除文本

我们可以使用os.Truncate方法删除文件的内容。 Truncate方法接收参数,例如文件路径字符串和要截断或设置的文件大小。如果我们将第二个参数设置为0,则文件大小为零,所有内容将被删除或删除。

package main

import (
    "log"
    "os"
)

func main() {
    /* test.txt
    Hi
    Hello
    World
    Gophers
    */
    err := os.Truncate("test.txt", 0)
    if err != nil {
        log.Fatal(err)
    }
    /* test.txt is empty
    */
}
$ cat test.txt
Hi
Hello
World
Gophers

$ go run delete.go

$ cat test.txt

我们可以看到,如果我们将Truncate方法的第二个参数(size)设置为0。

,则将文件的内容清空。

我们还可以将大小的值设置为要保留的字节数,因此,我们可以将其设置为n正整数,以仅保存文件中的第一个n字节。

package main

import (
    "log"
    "os"
)

func main() {
    /* test.txt
    Hi
    Hello
    World
    Gophers
    */
    err := os.Truncate("test.txt", 6)
    if err != nil {
        log.Fatal(err)
    }
    /* test.txt 
    Hi
    Hel
    */
}
$ cat test.txt
Hi
Hello
World
Gophers

$ go run delete.go

$ cat test.txt
Hi
Hel

因此,在上面的示例中,如果我们将大小参数设置为截断方法为6,则将文件的大小保持在6个字节上。因此,我们只看到Hi\nHel,新行是一个单个字节。其余内容被删除。这就是我们以前通过将大小设置为0的所有字节从文件中删除的方式。

就是这部分。所有代码示例和命令的参考可以在100 days of Golang GitHub存储库中找到。

结论

因此,从本系列的这一部分中,我们能够使用Golang在文件上执行写操作。我们使用了来自标准库中的软件包,并执行了诸如写,附加,覆盖,删除和替换为简单的文本文件,但它可能是任何文件格式。

感谢您的阅读。如果您有任何疑问,问题或反馈,可以在下面的讨论中或我的社交手柄上告诉我。快乐编码:)系列:“ [''100天Golang']