You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go如何结合tail与utfutil读取UTF16LE BOM格式实时日志

Go实时tail追踪UTF16LE BOM编码持续写入日志实现方案

现有代码的核心问题

你写的接近可用版本的问题根本不是defer Close失效——utfutil.OpenFile返回的编码读取器的Close()方法会自动关闭底层操作系统文件句柄,你写的defer file.Close()本身是正确的。真正导致功能异常的是两个逻辑缺陷:

  • 读到EOF时没有处理半行场景:游戏写入日志是逐块写入的,你可能在某行写到一半的时候触发读取,这时候拿到的半截内容会被误判为完整行,后续新写入的该行剩余内容会被拆成碎段
  • 循环读取时没有处理缓存残留:bufio触发EOF后,内部缓存的残留状态如果不做半行暂存,会导致后续新写入的内容可能被漏读

修复后的完整实现

代码直接适配EVE聊天日志场景,内置"die"关键词标记逻辑,CPU占用低,不会漏读、不会乱码、句柄可正常释放:

package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os"
	"strings"
	"time"

	"github.com/TomOnTime/utfutil"
)

func main() {
	logPath := "C:/Users/user/Documents/EVE/logs/Chatlogs/chat_20220709_022129_1006197774.txt"
	// 用utfutil打开自动识别UTF16LE BOM,转成标准UTF8流
	f, err := utfutil.OpenFile(logPath, utfutil.WINDOWS)
	if err != nil {
		fmt.Printf("打开日志文件失败: %v\n", err)
		return
	}
	defer f.Close()

	reader := bufio.NewReader(f)
	// 缓存未读完的半行内容
	var partialLine bytes.Buffer

	for {
		line, err := reader.ReadString('\n')
		if err != nil {
			if err == io.EOF {
				// 把当前读到的半截内容存到缓存,等下次写入补全
				if len(line) > 0 {
					partialLine.WriteString(line)
				}
				// 休眠500ms避免占满CPU
				time.Sleep(500 * time.Millisecond)

				// 检查文件是否被游戏轮转/删除,触发等待逻辑
				if _, statErr := os.Stat(logPath); os.IsNotExist(statErr) {
					fmt.Println("日志文件被轮转,等待新文件生成...")
					time.Sleep(1 * time.Second)
					continue
				}
				continue
			}
			fmt.Printf("读取文件出错: %v\n", err)
			break
		}

		// 拼接之前缓存的半行 + 当前读到的行,组成完整行
		fullLine := partialLine.String() + line
		partialLine.Reset()

		// 去掉首尾空白字符
		fullLine = strings.TrimSpace(fullLine)
		if len(fullLine) == 0 {
			continue
		}

		// 打印所有读到的日志行
		fmt.Println(fullLine)

		// 匹配包含"die"的聊天内容,不区分大小写
		if strings.Contains(strings.ToLower(fullLine), "die") {
			fmt.Printf("*** 命中关键词: %s ***\n", fullLine)
		}
	}
}

适配说明

  • 编码适配:utfutil.WINDOWS模式会自动识别EVE日志的UTF16LE BOM头,输出标准UTF8文本,完全不会出现乱码
  • 性能表现:固定500ms轮询间隔,CPU占用可以忽略,完全满足游戏日志实时性要求
  • 鲁棒性:内置半行缓存、文件存在性检测逻辑,不会因为游戏切日志、写入半行就出现解析错误
  • 资源释放:defer f.Close()会正确关闭底层文件句柄,不会出现资源泄漏

如果需要从文件末尾开始读、不加载历史日志内容,可以在打开文件后先循环读取直到第一次EOF,再进入正式的轮询逻辑即可,和现有代码完全兼容。


内容的提问来源于stack exchange,提问作者Gaudard

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 21:54:18