Golang中os.OpenFile为何会在文件中添加乱码

Golang中os.OpenFile为何会在文件中添加乱码 每次我使用 os.Open 打开文件时,都会在文件中添加空格和换行符。这些垃圾内容原本并不存在于文件中。我该如何解决这个问题?

package main

import (
	"fmt"
	"os"
)

func main() {
	file, err := os.OpenFile("log.txt", os.O_APPEND|os.O_WRONLY, 0644)
	if err != nil {
		fmt.Println(err.Error())
	}

	file.WriteString("test")

	timestamp, _ := ioutil.ReadFile("log.txt")
	tpl.ExecuteTemplate(w, page, string(timestamp))
}

实际输出:

image

期望输出:

image


更多关于Golang中os.OpenFile为何会在文件中添加乱码的实战教程也可以访问 https://www.itying.com/category-94-b0.html

13 回复

你在写入后关闭,所以这应该没问题,并且是“同步”的。

更多关于Golang中os.OpenFile为何会在文件中添加乱码的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


是的,你说得对。你不需要关闭输出文件,但至少应该确保在从它(文件)读取数据之前,输出缓冲区已被刷新。

如果这是真的,那为什么操作系统会有一个同步函数呢?

Sync 将文件的当前内容提交到稳定存储。通常,这意味着将文件系统内存中最近写入的数据副本刷新到磁盘。

直接文件访问是一个■■■■■■■,在语言中实现为无缓冲流的内容,在内核或文件系统驱动层面可能被缓冲。

然而,语言本身对此无能为力,因为与内核的接口是无缓冲的。

在你的应用程序和实际的磁盘之间,存在着许多抽象层。

其中一些会延迟和缓冲读写操作,而另一些则不会。

你最好的选择是不要读取“脏”文件,这些文件已经为写入而打开。

观察你的第一次尝试……你打开文件并向其写入,然后“再次”打开该文件并从中读取。

我会尝试打开文件并向其写入,然后关闭文件。 接着,我会尝试打开文件并从中读取,然后关闭文件。

通常,这意味着将文件系统中最近写入数据的内存副本刷新到磁盘。

我如何使用“os”来刷新文件?

直接文件访问是一个■■■■■■■,在语言中实现为无缓冲流的内容,可能在内核或文件系统驱动层被缓冲。

你能进一步详细说明这一点吗?

输出缓冲区已被刷新

如何刷新 os?我读到以下内容:

为什么 os.File 中没有 flush 函数?

你会注意到 os.File 没有 .Flush() 方法,因为它不需要,它是无缓冲的。对它的写入是直接进行系统调用来写入文件的。

我以为我已经说得很清楚了。使用 Sync 方法来刷新输出缓冲区。

func (f *File) Sync() error

Sync 将文件的当前内容提交到稳定存储。通常,这意味着将文件系统内存中最近写入数据的副本刷新到磁盘。

来自这里:os package - os - pkg.go.dev

你最好的选择是不要读取“脏”文件,即那些已为写入而打开的文件。

那我应该怎么做呢?

func crondoc(msg string) {
  file, err := os.OpenFile("log.txt", os.O_APPEND|os.O_WRONLY, 0644)
  if err != nil {
    fmt.Println(err.Error())
  }
  
  file.WriteString(msg + "\n")
  defer file.Close()
}

Sibert 说: 去掉这个

问题的原因仍然是个谜。我认为这是 Go 添加的,但我无法确认。但一个快速的解决方法是向 CSS 中添加一行代码来移除这些空格。

white-space: pre-line;

来源: cron.go4webdev.org 网站图标: 网站图标

goCron

goCron 自动化任务

之前:

image

之后:

image

仅供参考

你打开文件进行写入,然后又“再次”打开该文件进行读取。

这样是否更好:

package main

import (
  "fmt"
  "github.com/robfig/cron"
  "os"
  "time"
)

func crontsk() {
  c := cron.New()
  c.AddFunc("@every 5s", addstamp)
  c.AddFunc("@every 60s", cleardoc)
  c.Start()
  time.Sleep(time.Duration(1<<63 - 1)) // 运行超过260年
}

func addstamp() {
  t := time.Now()
  t.String()
  crondoc(t.Format("2006-01-02T15-04-05.000Z"))
}

func crondoc(msg string) {
  file, err := os.OpenFile("log.txt", os.O_APPEND|os.O_WRONLY, 0644)
  if err != nil {
    fmt.Println(err.Error())
  }
  
  file.WriteString(msg + "\n")
  defer file.Close()
}

func cleardoc() {
  if err := os.Truncate("log.txt", 0); err != nil {
    fmt.Println("Failed to truncate: %v", err)
  }
}

goCron

goCron

goCron 自动化任务

据我所知,在使用 ioutil 时不必关闭文件?

os.OpenFile 本身不会在文件中添加乱码。问题出现在你的代码逻辑中:文件没有正确关闭,导致缓冲区内容没有完全写入磁盘。当你立即读取文件时,可能读取到的是未刷新的缓冲区内容。

以下是修正后的代码:

package main

import (
    "fmt"
    "io/ioutil"
    "os"
)

func main() {
    // 1. 使用正确的标志位打开文件
    file, err := os.OpenFile("log.txt", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
    if err != nil {
        fmt.Println(err.Error())
        return
    }
    defer file.Close() // 3. 确保文件关闭
    
    // 2. 写入数据
    _, err = file.WriteString("test")
    if err != nil {
        fmt.Println(err.Error())
        return
    }
    
    // 4. 强制刷新到磁盘(可选,Close()会自动执行)
    file.Sync()
    
    // 5. 重新打开文件读取,确保获取最新内容
    timestamp, err := ioutil.ReadFile("log.txt")
    if err != nil {
        fmt.Println(err.Error())
        return
    }
    
    fmt.Println(string(timestamp))
}

关键修改点:

  1. 添加 os.O_CREATE 标志:确保文件不存在时会被创建
  2. 添加错误处理:检查 WriteString 的返回值
  3. 使用 defer file.Close():确保文件描述符被释放,缓冲区内容刷新到磁盘
  4. 调用 file.Sync():强制将内容同步到磁盘(在需要立即读取的场景下很有用)
  5. 重新打开文件读取:避免读取未刷新的文件句柄

常见问题排查:

如果问题仍然存在,检查:

  • 文件是否被其他进程占用
  • 磁盘空间是否充足
  • 文件权限是否正确
// 调试方法:检查文件大小
info, _ := file.Stat()
fmt.Printf("File size: %d bytes\n", info.Size())

注意ioutil.ReadFile 会读取整个文件,包括可能存在的BOM头或其他不可见字符。使用 hex.Dump 可以查看原始字节:

import "encoding/hex"
fmt.Printf("%s", hex.Dump(timestamp))
回到顶部