欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 焦点 > Golang 刷算法题:标准输入处理的常见陷阱与解决方案

Golang 刷算法题:标准输入处理的常见陷阱与解决方案

2025/2/21 3:16:33 来源:https://blog.csdn.net/qq_67733273/article/details/145647618  浏览:    关键词:Golang 刷算法题:标准输入处理的常见陷阱与解决方案

文章目录

  • 前言
  • 读取方式
    • 1. fmt.Scan
        • 方法描述
        • 代码解释
    • 2. bufio.NewScanner(os.Stdin)
        • 方法描述
        • 代码解释
  • 踩坑及解决方案
    • 1. fmt.Scan 读取速度慢
        • 问题描述
        • 解决方案
    • 2. bufio.NewScanner 的容量限制
        • 问题描述
        • 解决方案
        • 代码解释
    • 3. bufio.NewScanner 处理多数字输入繁琐
        • 问题描述
        • 解决方案
        • 代码解释
  • 踩坑
  • 总结建议
    • 1. 优先选择 bufio.NewScanner(os.Stdin)
    • 2. 按需调整缓冲区大小
    • 3. 灵活使用分割方式
  • 最后

前言

你好,我是醉墨居士。在最近使用 Golang 刷牛客网算法题的过程中,处理标准输入时遇到了不少棘手的问题。这些问题一度让我的解题之路变得磕磕绊绊,但通过不断地探索和尝试,我成功找到了有效的解决方案。现在,我迫不及待地想把这些经验分享给大家,希望能帮助各位在使用 Golang 刷题时更加顺畅,少走一些弯路

读取方式

我们主要读取标准输入的方式可以分为以下两种:

1. fmt.Scan

方法描述

fmt.Scan 是 Go 语言标准库 fmt 包提供的一个输入函数,它以空白字符(如空格、制表符、换行符等)作为输入值的分隔符。以下是一个简单的示例,该示例从标准输入读取两个整数,并计算它们的和:

package mainimport ("fmt""io"
)func main() {var a, b int// 循环读取输入for {// 尝试读取两个整数到变量 a 和 b_, err := fmt.Scan(&a, &b)// 如果遇到文件结束符(EOF),则跳出循环if err == io.EOF {break}// 打印两个整数的和fmt.Println(a + b)}
}
代码解释
  • 首先定义了两个整数变量 a 和 b,用于存储从标准输入读取的整数
  • 使用 for 循环不断尝试读取输入,fmt.Scan(&a, &b) 会尝试从标准输入读取两个整数,并将它们分别赋值给 a 和 b
  • 如果读取过程中遇到文件结束符(EOF),则 err 会等于 io.EOF,此时跳出循环
  • 若读取成功,则打印 a 和 b 的和

2. bufio.NewScanner(os.Stdin)

方法描述

bufio.NewScanner(os.Stdin) 用于创建一个从标准输入读取数据的扫描器,默认情况下它按行读取输入。以下是一个示例,同样实现读取两个整数并计算它们的和:

package mainimport ("bufio""fmt""os""strconv""strings"
)func main() {var a, b int// 创建一个从标准输入读取数据的扫描器input := bufio.NewScanner(os.Stdin)// 循环读取每一行输入for input.Scan() {// 将当前行按空格分割成字符串切片parts := strings.Split(input.Text(), " ")// 将第一个字符串转换为整数并赋值给 aa, _ = strconv.Atoi(parts[0])// 将第二个字符串转换为整数并赋值给 bb, _ = strconv.Atoi(parts[1])// 打印两个整数的和fmt.Println(a + b)}
}
代码解释
  • 首先定义了两个整数变量 a 和 b
  • 使用 bufio.NewScanner(os.Stdin) 创建一个扫描器,用于从标准输入读取数据
  • 通过 for input.Scan() 循环逐行读取输入,input.Text() 可以获取当前行的文本内容
  • 使用 strings.Split(input.Text(), " ") 将当前行按空格分割成字符串切片
  • 使用 strconv.Atoi() 函数将分割后的字符串转换为整数,并分别赋值给 a 和 b
  • 最后打印 a 和 b 的和

踩坑及解决方案

在使用上述两种读取方式时,可能会遇到一些问题,下面将详细介绍这些问题及相应的解决方案

1. fmt.Scan 读取速度慢

问题描述

在一些算法题中,当算法的时间复杂度已经优化到极致时,使用 fmt.Scan() 读取输入数据仍然可能会导致超时。这是因为 fmt.Scan 在读取输入时会进行较多的格式化和解析操作,效率相对较低

解决方案

建议使用 bufio.NewScanner() 来替代 fmt.Scan。bufio.NewScanner() 采用了缓冲区机制,能够更高效地读取输入数据,从而避免因读取输入而导致的超时问题

2. bufio.NewScanner 的容量限制

问题描述

默认情况下,bufio.NewScanner 存在容量限制,其最大读取长度为 bufio.MaxScanTokenSize,即 64 * 1024 = 65536 个字节。如果读取的一行字符串的字节长度超过 65536,就会报错 bufio.Scanner: token too long,并且无法读取到完整的一行数据。

解决方案

可以使用 bufio.Scanner.Buffer() 方法来增加缓冲区的最大读取大小。以下是一个示例:

package mainimport ("bufio""fmt""os"
)func main() {input := bufio.NewScanner(os.Stdin)// 增加缓冲区大小为 1024 * 1024 字节buffer := make([]byte, 1024*1024)input.Buffer(buffer, 1024*1024)for input.Scan() {fmt.Println(input.Text())}
}
代码解释
  • 首先创建一个 bufio.Scanner 对象
  • 然后使用 make([]byte, 1024*1024) 创建一个大小为 1024 * 1024 字节的缓冲区
  • 调用 input.Buffer(buffer, 1024*1024) 方法将缓冲区设置为 buffer,并将最大读取大小设置为 1024 * 1024 字节
  • 最后通过循环逐行读取输入并打印

3. bufio.NewScanner 处理多数字输入繁琐

问题描述

对于某些题目,如果输入的某一行包含多个数字,且这些数字以空格分隔,使用 bufio.NewScanner 时需要先读取一行,然后使用 strings.Split 方法进行分割,再逐个将字符串转换为数字,操作相对繁琐

解决方案

可以使用 bufio.Scanner.Split(bufio.ScanWords) 方法让输入以空白字符(空格、制表符、换行符等)作为分隔符,这样可以简化输入数据的处理。以下是一个示例:

package mainimport ("bufio""fmt""os""strconv"
)func main() {input := bufio.NewScanner(os.Stdin)// 设置分割方式为按单词分割input.Split(bufio.ScanWords)for input.Scan() {num, _ := strconv.Atoi(input.Text())fmt.Println(num)}
}
代码解释
  • 首先创建一个 bufio.Scanner 对象
  • 调用 input.Split(bufio.ScanWords) 方法将分割方式设置为按空白字符进行分割
  • 通过循环逐个读取单词,并将其转换为整数后打印
  • 需要注意的是,如果题目的输入数据是以 , 或者其他非空白字符进行分隔的,就不能使用这种方式。不过这种情况出现的概率相对较低

踩坑

  1. 问题:使用fmt.Scan()读输入数据很慢,在某些题目中算法的时间复杂度已经优化到极致了,也会出现超时
    解决方案:使用bufio.NewScanner()

  2. 问题:使用默认的bufio.NewScanner()是存在容量限制的,bufio.MaxScanTokenSize = 64*1024 = 65536个,也就是说默认情况下只能读取65536个byte的长度,如果读取的一行字符串的字节长度超过65536,就会报错bufio.Scanner: token too long,并且也无法读取到完整的一行数据
    解决方案:使用bufio.Scanner.Buffer() 增加缓冲区的最大读取大小

  3. 问题:使用bufio.NewScanner对于某些题来说,如果输入的某一行会有多个数字按照空格分隔,我们需要首先读取一行,然后string.Split,然后在逐个索引下标,然后在字符串转数字,操作还是比较繁琐的
    解决方案:使用bufio.Scanner.Split(bufio.ScanWords) 让输入以空白字符(空格、制表符、换行符等)做为分隔,但请注意如果题目的输入数据是以 ‘,’ 或者其它非空白字符进行分隔的,就不能使用这种方式,不过这种情况出现概率会很低,至少我到现在还没有遇到过,如果不幸遇到了,那就没办法了,只能每次读取数据进行略微繁琐的预处理

总结建议

1. 优先选择 bufio.NewScanner(os.Stdin)

在处理标准输入时,建议优先使用 bufio.NewScanner(os.Stdin) 来读取数据。因为它比 fmt.Scan 更快,能够有效避免在一些题目中因读取输入而造成的超时问题

2. 按需调整缓冲区大小

根据题目的具体要求,如果一次读取的输入数据长度可能超过 65536 字节,就要使用 bufio.Scanner.Buffer() 方法来增加缓冲区的大小,防止溢出

3. 灵活使用分割方式

对于一些输入数据完全使用空白字符分隔的题目,可以使用 bufio.Scanner.Split(bufio.ScanWords) 方法以空白字符进行分隔,简化输入数据的处理

最后

希望通过以上的分享,能让大家在使用 Golang 刷算法题时更加得心应手。如果在实践过程中遇到任何问题,欢迎随时交流探讨。祝大家刷题顺利,早日攻克各种难题

如果你还有遇到其它类型的问题,欢迎在评论区进行留言

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词