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

如何优化基于bufio.NewScanner的脚本读取Go程序性能

优化方案

你认为scanner.Bytes()不适用的担心是多余的,strings.Builder原生支持写入字节切片,全程使用字节操作仅在最后调用.String()时做一次类型转换,反而能省掉每次循环中scanner.Text()产生的字符串拷贝开销,核心优化点如下:

  • 替换scanner.Text()为scanner.Bytes(),所有行处理逻辑直接基于字节切片完成,避免每次循环的字符串内存拷贝开销。
  • 移除行拼接逻辑:原代码中lines + "; "会生成临时字符串产生额外拷贝,改为分两次写入行内容和固定后缀,消除临时对象开销。
  • 预分配strings.Builder缓冲区:提前获取文件大小,调用.Grow()方法预留足够的内存空间,避免运行时动态扩容带来的多次内存拷贝。
  • 优化判断逻辑:空行直接判断字节切片长度是否为0,注释行直接判断切片首字节是否为#,行尾判断直接取字节切片最后一个字符对比,比字符串操作效率更高。
  • 新增scanner.Err()错误处理,补全原代码缺失的读取错误捕获逻辑。

优化后的代码如下:

package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"strings"
)

func main() {
	filePath := "./script.sh"
	file, err := os.Open(filePath)
	if err != nil {
		log.Fatalln(err)
	}
	defer file.Close()

	// 获取文件大小用于预分配缓冲区
	fileInfo, err := file.Stat()
	if err != nil {
		log.Fatalln(err)
	}
	var builder strings.Builder
	// 预留略大于文件的空间,覆盖添加的分号和空格开销,避免扩容
	builder.Grow(int(fileInfo.Size() + 1024))

	scanner := bufio.NewScanner(file)
	// 预定义后缀字节切片,避免重复生成
	semicolonSuffix := []byte("; ")
	spaceSuffix := []byte(" ")

	for scanner.Scan() {
		lineBytes := scanner.Bytes()
		switch {
		case len(lineBytes) == 0 || lineBytes[0] == '#':
			continue
		case lineBytes[len(lineBytes)-1] != ';':
			builder.Write(lineBytes)
			builder.Write(semicolonSuffix)
		default:
			builder.Write(lineBytes)
			builder.Write(spaceSuffix)
		}
	}

	// 处理扫描过程中可能出现的错误
	if err := scanner.Err(); err != nil {
		log.Fatalln(err)
	}

	fmt.Println(builder.String())
}

以上优化在处理KB级以上的脚本文件时会有明显的性能提升,文件越大优化效果越显著。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.15 03:54:07