使用Golang从stdin读取长文本的最佳实践

使用Golang从stdin读取长文本的最佳实践 我想使用 Golang 的 stdin 模块输入并读取超过 10000 个字符的字符串。

然而,根据我目前对 Golang 的了解,我只能读取前 4096 个字符。

因此,我希望有人能帮助我解决这个问题。

谢谢。

4 回复
  • 如果需要逐行处理输入,使用 bufio.Reader 更为合适。
  • 如果需要将整个输入读取为单个字符串,且不关心内存使用情况,io.ReadAll 则更简单。

更多关于使用Golang从stdin读取长文本的最佳实践的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你当前的方法是什么?实际上,从标准输入读取数据应该没有大小限制。

使用 os.ReadAll 来读取 os.Stdin 应该是可行的。

package main

import (
	"io"
	"os"
)

func main() {
	res, err := io.ReadAll(os.Stdin)
	if err != nil {
		panic(err)
	}

	println(len(res))
}

它将标准输入读取到一个缓冲区,并将其追加到结果缓冲区,直到发生错误。如果错误是 io.EOF,则返回收集到的缓冲区。否则,返回该错误。它的工作方式有点像下面的代码片段。

package main

import (
	"io"
	"os"
)

func main() {
	buf := make([]byte, 1024)
	var res []byte

	for {
		n, err := os.Stdin.Read(buf)
		res = append(res, buf[:n]...)
		if err == io.EOF {
			break
		}
		if err != nil {
			panic(err)
		}
	}

	println(len(res))
}

希望这能有所帮助。

你好,@zekro 首先,感谢你的回复。

我正在尝试完成这个 Hackerrank 任务:Goodland Electricity

但对于这个测试用例,我需要先从标准输入读取 100000 个字符。

所以如果可能的话,我希望你能帮我修复这个测试用例的问题。

谢谢。

我想使用 Golang 的 stdin 模块输入并读取一个超过 10000 个字符的字符串。

然而,以我目前对 Golang 的了解,我只能读取前 4096 个字符。

所以我希望有人能帮我解决这个问题。

谢谢。

在Go中从stdin读取长文本时,常见的bufio.Scanner默认缓冲区大小为4096字节,这解释了您遇到的情况。以下是几种处理长文本输入的方法:

方法1:增大Scanner的缓冲区大小

package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    scanner := bufio.NewScanner(os.Stdin)
    
    // 增大缓冲区大小到64KB(可根据需要调整)
    buf := make([]byte, 0, 64*1024)
    scanner.Buffer(buf, 1024*1024) // 最大容量设为1MB
    
    var input string
    for scanner.Scan() {
        input += scanner.Text() + "\n"
    }
    
    if err := scanner.Err(); err != nil {
        fmt.Fprintf(os.Stderr, "读取错误: %v\n", err)
        return
    }
    
    fmt.Printf("读取字符数: %d\n", len(input))
}

方法2:使用io.ReadAll(Go 1.16+)

package main

import (
    "fmt"
    "io"
    "os"
)

func main() {
    // 一次性读取所有输入
    data, err := io.ReadAll(os.Stdin)
    if err != nil {
        fmt.Fprintf(os.Stderr, "读取错误: %v\n", err)
        return
    }
    
    input := string(data)
    fmt.Printf("读取字符数: %d\n", len(input))
    fmt.Printf("前100个字符: %s\n", input[:min(100, len(input))])
}

func min(a, b int) int {
    if a < b {
        return a
    }
    return b
}

方法3:使用bufio.Reader分块读取

package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    reader := bufio.NewReader(os.Stdin)
    var input []byte
    buf := make([]byte, 4096) // 每次读取4KB
    
    for {
        n, err := reader.Read(buf)
        if n > 0 {
            input = append(input, buf[:n]...)
        }
        if err != nil {
            if err.Error() == "EOF" {
                break
            }
            fmt.Fprintf(os.Stderr, "读取错误: %v\n", err)
            return
        }
    }
    
    fmt.Printf("读取字符数: %d\n", len(input))
}

方法4:处理超长输入(流式处理)

package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    scanner := bufio.NewScanner(os.Stdin)
    
    // 设置非常大的缓冲区
    maxCapacity := 10 * 1024 * 1024 // 10MB
    buf := make([]byte, maxCapacity)
    scanner.Buffer(buf, maxCapacity)
    
    // 逐行处理(适合超大文件)
    lineCount := 0
    totalChars := 0
    
    for scanner.Scan() {
        line := scanner.Text()
        lineCount++
        totalChars += len(line)
        
        // 这里可以处理每一行
        fmt.Printf("第%d行: %d字符\n", lineCount, len(line))
    }
    
    if err := scanner.Err(); err != nil {
        fmt.Fprintf(os.Stderr, "错误: %v\n", err)
        return
    }
    
    fmt.Printf("总计: %d行, %d字符\n", lineCount, totalChars)
}

测试示例

# 生成测试数据(10万字符)
echo "$(python3 -c "print('A' * 100000)")" | go run main.go

# 或者从文件重定向
go run main.go < large_text.txt

对于超过4096字符的输入,推荐使用方法1(调整Scanner缓冲区)或方法2(io.ReadAll)。如果输入可能非常大(超过内存限制),使用方法4的流式处理更为合适。

回到顶部