☰
Python3 ACM模式输入输出全攻略:从input到sys.stdin,牛客华为机试必看
2026/10/3 10:05:13 网站建设 项目流程

如果你正在用 Python3 刷牛客网,或者准备华为机试,八成会遇到一个魔幻场景:本地 IDE 里跑得好好的代码,一贴进在线编辑器,提交直接 0 分。排查半天发现,问题根本不在算法,而是 ACM 模式下的输入输出没处理好。市面上的题解大多只贴核心函数,可笔试平台要的是完整程序:自己从标准输入读数据,再把结果输出到标准输出。这篇文章就把这层窗户纸捅破,从最基础的 input() 到 sys.stdin 的性能优化,再到牛客网和华为机试里的高频输入场景模板,一次性讲透。适合准备求职机试、算法竞赛,以及刚转用 Python3 刷题的同学。

1. ACM模式到底在考什么

1.1 核心代码模式和ACM模式的本质区别

先讲清楚一个很多人懵了很久的概念差。核心代码模式(国内一般叫“函数模式”,力扣那种)是平台把输入解析好,把数据当成参数传给你,你只需要实现某个类的某个方法,最后 return 结果。而 ACM 模式正好反过来,平台给你一堆标准输入文本,你要自己负责把数据从 stdin 里读出来,解析成想要的变量,计算完之后再把结果通过 stdout 打印出去,由评测系统比对输出文件。

听起来很简单,但两种模式对人的要求完全不同。核心代码模式就像去食堂窗口点菜,你告诉师傅要什么菜,师傅把盛好的餐盘递给你;ACM 模式则是自助餐,菜都在台子上摆着(标准输入),你得自己拿盘子(申请变量)、自己夹菜(解析数据)、自己决定怎么吃(算法逻辑),最后还得把盘子放到指定位置(格式化输出)。很多习惯了函数模式的人,一换到 ACM 模式就手足无措,不是不会写算法,是根本不知道数据该怎么读进来。

我整理了一个对比表,方便直观感受:

对比维度核心代码模式ACM模式
输入来源函数参数传入标准输入 stdin
输出方式return 返回值标准输出 stdout
代码结构实现指定函数/类完整可运行程序
代表平台力扣、部分赛码网题目牛客网、华为机试、竞赛
主要考察算法思维算法思维 + 完整工程处理能力

这里要特别提醒一句:笔试环境下,很多公司笔试系统用的是牛客或赛码这类平台,它们默认就是 ACM 模式。你光在力扣上刷题刷得再熟,如果从没练过完整读入输出,上考场大概率要吃亏。

1.2 为什么牛客网和华为机试都爱用ACM模式

其实这不是平台刻意刁难人,而是有实际原因的。第一批原因在于评测机制:ACM 模式下,平台只需要生成输入文件,运行你的程序,然后把你的 stdout 和预先算好的标准答案文件做字符级比对,整个系统实现非常统一,不依赖任何语言绑定。反观核心代码模式,平台就得为每种语言写一套适配层,把输入数据序列化成参数再塞进你的函数,工程复杂度高一截。

第二个原因是防止作弊和增强随机性。ACM 模式可以给不同考生生成不同的测试数据,输入规模、数据顺序都能动态变。比如同一道排序题,你的程序必须能处理不同长度的输入行,不能写死。这更接近真实工作中处理外部数据流的状态——你写出来的脚本要能扛得住千奇百怪的输入格式,而不是只在固定参数上能用。

第三个原因可能更现实:很多机试系统是直接从算法竞赛平台改过来的,底子就是 ACM 风格。牛客网的在线编程模块、华为 OD 机试系统,从小范围到大厂招聘,基本都继承了这套“标准输入输出 + 评测机比对”的玩法。你与其抱怨,不如把 IO 这块练成肌肉记忆,省下考试时的脑力去思考真正的算法问题。

1.3 机试读题的第一件事:看输入输出描述

千万不要一拿到题目就埋头写代码。ACM 模式题目通常会在“输入描述”里给出明确格式,比如“第一行包含一个整数 T,表示测试数据的组数”“接下来 T 行,每行包含两个整数 a 和 b”。在读题阶段,你要先判断出这属于哪一类输入场景,然后直接套对应的模板,而不是现场想。

有一次我带一个学弟准备机试,他卡在一道很简单的题上半小时,原因是题目写“输入包含多行,每行包含两个整数”,他没意识到这是 EOF 读到底的模式,非要去数一共有几组,结果用 int(input()) 反复读,一提交就崩。所以我会在下一节把所有常见场景的模板整理出来,先背熟,再谈其他。

2. 输入基础:从 input() 到 sys.stdin

2.1 input() 的常见用法和暗坑

Python3 里最无脑的读入方式就是 input()。它一次读取一行文本,自动去掉末尾的换行符,返回一个字符串。需要注意,它不会自动去掉行首尾的空格,如果你读的是「一行里有多个空格分隔的数字」,必须手动 split。绝大多数题目里输入文本规规矩矩,input() 够用,但它有几个暗坑值得先说。

第一个坑:读到文件末尾时会抛 EOFError。比如题目要求“处理到文件结束”,如果你用 while True 死循环 + input(),最后就会崩溃。第二个坑:input() 默认读整行,如果一行里既有整数又有字符串,不要直接 int(input()),要整体读进来再拆。第三个坑:它不是最快的读入方式,一旦数据量到几十万行级别,性能就会有明显差距。

我平时最推荐的三种 input() 写法:

# 读一个整数 n = int(input()) # 读一行空格分隔的整数 nums = list(map(int, input().split())) # 读一个整数和一个字符串,再读一行字符串 k = int(input()) s = input().strip()

第三行里为什么加 .strip()?因为 input() 虽然去掉末尾换行,但如果这一行有行尾空格,或者你想统一去除首尾空白,strip() 是更稳妥的。不过也要注意,如果题目明确告诉你某个字符串里的空格是有意义的,那你不能随便 strip(),只能手动按字符处理。

2.2 sys.stdin:机试提速的关键一步

当你的代码在大数据上超时,先别急着优化算法,先看看读入是不是太慢。很多人不知道,Python3 的 input() 是有额外开销的,每次调用都要做编码解码、处理提示字符串等操作,而 sys.stdin 本身是一个缓冲流对象,直接调用它的 readline() 或 read() 效率要高不少。

举个例子。有次帮人排查一道数组统计题,数据量接近 100 万行,用 input() 在本地跑要 4 秒多,换成 sys.stdin 直接读不到 1 秒就完成了。这种差距在算法题里是非常致命的,因为很多机试限时 1 到 2 秒,读入慢基本等于死刑。

最常用的两套 sys.stdin 写法:

import sys # 逐行读 for line in sys.stdin: line = line.strip() if not line: continue # 这一行自己按需要解析 # 全量读,按空白字符切分 data = sys.stdin.read().split()

第二种写法尤其适合“第一行给 n,第二行给 n 个数”这种固定结构的数据。你把所有输入按空白字符切成一个列表,之后需要什么就按顺序取什么,完全不用关心换行符在哪。很多竞赛选手就是这么写的,代码短、速度快、可读性也好。

2.3 输出格式的细节决定生死

输出看似简单,其实也有讲究。print() 默认会在末尾加上换行,多个值用逗号分隔时自动用空格连接。比如 print(a, b) 会输出“a b\n”,这在大多数情况没问题。但如果题目要求输出一个列表,比如所有答案用空格分隔成一行,最稳的写法是:

print(" ".join(map(str, ans)))

join 的好处是不会在结尾多出一个空格,也不会像循环 print(..., end=' ') 那样最后多一个空格。部分评测系统对行尾空格很宽容,但也有系统是严格字符匹配,一旦多一个空格就判 WA。为了稳妥,我统一用 join。

另一个常见要求是保留小数位数。Python 里用 f-string 很方便:

print(f"{ans:.2f}")

不过要注意 round() 是“银行家舍入”,遇到 0.5 这种边界值可能向偶数方向取整,和你想的四舍五入有偏差。机试里大部分题目不会专门卡这个边界,但如果你不放心,可以自己加一个极小量再格式化,比如 print(f"{ans + 1e-9:.2f}")。我自己在比赛中就这么干过,能避开不少奇怪的精度问题。

3. 高频输入场景模板大全

3.1 单行输入的四类情况

单行输入是机试里最简单也最常见的形态。归纳一下,无非四种:单个整数、一行多个整数、一行字符串、一行混合数据。我直接把模板贴出来:

# 1. 单个整数 n = int(input()) # 2. 一行多个整数 nums = list(map(int, input().split())) # 3. 一行字符串 s = input().strip() # 4. 一行混合数据,比如先是整数n,再是字符串s parts = input().split() n = int(parts[0]) s = parts[1]

注意第四种写法里,整行先用 split() 切成字符串列表,再逐个转换。千万不要写成 int(input().split()),这一定会报 TypeError,因为 split() 返回的是列表而不是字符串。

3.2 多行输入的四种循环套路

多行输入才是机试的重头戏。我总结了四种最经典的场景,基本覆盖牛客和华为机试里 90% 以上的输入形态。

第一种:先给一个整数 T,表示接下来有 T 行数据。这是最常见的“测试用例组数”模式。

T = int(input()) for _ in range(T): nums = list(map(int, input().split())) # 处理每一组数据

第二种:不知道具体多少组,一直读到文件结束(EOF)。牛客网特别喜欢这种格式,经典题目比如“每行输入两个整数 a 和 b,计算 a + b,输入包含多行”。

import sys for line in sys.stdin: line = line.strip() if not line: continue a, b = map(int, line.split()) print(a + b)

这里有个小细节:for line in sys.stdin 遇到文件结束会自动停止,你不需要手动判断 EOF,也不会抛异常。如果你非要用 input() 模拟这种效果,就得写 try/except EOFError,麻烦且容易出问题。

第三种:不确定组数,但会遇到一个特殊结束符,比如输入 0 0 时停止。

while True: line = input().strip() if line == "0 0": break a, b = map(int, line.split()) print(a + b)

这种写法要注意结束条件的字符串格式必须和输入完全一致。上面的例子假设结束符是“0 0”,如果题目说“输入 0 0 时结束”,那 line 去掉首尾空格后正好是 “0 0”,判断没问题。

第四种:先给行数和列数,再给一个矩阵。

n, m = map(int, input().split()) grid = [] for _ in range(n): row = list(map(int, input().split())) grid.append(row)

矩阵题在华为机试里很常见,尤其是 DFS、BFS 这种图的遍历题。你需要保证每一行读进来的长度和 m 一致,如果不一致,多半是输入里有空行混进去了,提前做防御性判断会省很多调试时间。

3.3 矩阵和字符串两类特殊输入

矩阵输入除了上面那种标准形式,偶尔会遇到没有行列数、直接给若干行的情况,这时就要靠 EOF 读了:

import sys grid = [] for line in sys.stdin: line = line.strip() if not line: continue row = list(map(int, line.split())) grid.append(row)

字符串输入则要格外小心空格。如果题目告诉你字符串里可能包含空格,比如“输入一个字符串,其中可能包含空格”,这时候不能 split(),只能去掉末尾换行:

import sys for line in sys.stdin: s = line.rstrip("\n") # 按行处理,保留行内空格

我在机试里遇到过一次很恶心的题,输入是一行用引号包裹的字符串,类似 "hello world" 这样带前后引号。这时候需要先 strip() 去掉两端空白,再手动去掉首尾引号,才可以拿到真实字符串。这种怪题目不多,但一旦碰到,平时积累的模板就能救命。

3.4 万能读入解析法:一梭子读完再处理

如果你觉得前面的模板太多记不住,我给你一个终极方案:用 sys.stdin.read() 把整个输入当成字符串一次性读完,然后按空白字符切分,再按输入描述的顺序一个个取数。这是一种很多竞赛选手都会用的“万能读入法”。

import sys def solve(): data = sys.stdin.read().split() it = iter(data) n = int(next(it)) nums = [int(next(it)) for _ in range(n)] # 继续按顺序取…… return result if __name__ == "__main__": print(solve())

这个写法的核心思路是:把输入看作一个连续的 token 流,完全忽略换行和行的概念。只要你知道输入的第一个字段是 n、第二个到第 n+1 个是数组元素,就能通过 next() 按顺序取出来。对于输入格式复杂、行数不固定的题目,这个方案往往比逐行分析简单得多。

我第一次见识这个思路是在打校赛的时候,隔壁老手不到一分钟就把 IO 部分写完了,我当时还在一行行 if line 判断。后来我也改用这个办法,刷题效率提升非常明显。当然,如果你要处理的是带空格的字符串,这个方案就不太灵了,因为 split() 会把空格当作分隔符给切开。所以万能读入法更适合纯数字类题目,字符串题还是老实按行读。

4. 牛客网和华为机试的备考节奏

4.1 牛客网在线编程的输入输出规律

牛客网的在线编程模块把题目分成了好几个系列,其中有一个“在线编程输入输出专项训练”,专门练各种输入格式,我强烈建议刚开始接触 ACM 模式的考生先去把这个专项刷一遍。里面的题目本身很简单,很多就是 A+B 的变体,但输入格式花样很多,正好用它们来练模板。

从我的经验看,牛客网常见的输入格式主要集中在三种:一种是“输入多组数据,每组一行”,需要读到 EOF;一种是“第一行为 T,接下来 T 行数据”;还有一种是“输入 0 0 结束”。你把这三种模板背熟,牛客上的大部分题目 IO 部分基本稳了。另外牛客的判题系统对输出格式比较宽松,行尾空格一般不影响,但我仍然建议你严格按照题目要求输出,别给自己埋雷。

4.2 华为机试的ACM模式细节

华为机试,尤其是大家常说的 OD 机试,一般考 3 道算法题,时间和分制不同批次会有差异,常见的大致是 100 分、200 分题目搭配,总时长两小时左右,各批次不完全一样。从题型风格上看,字符串处理、数组/矩阵、动态规划、DFS/BFS、排序和贪心是高频方向。

输入输出形式上,我见过的批次大多采用 ACM 模式,需要你自己处理 stdin/stdout。有些平台也会支持核心代码模式,但我不建议你赌这个,因为一旦遇到 ACM 模式的题,你不可能现场去翻模板。考试前要做好两手准备:既能写完整的读入输出,也熟悉函数模式下怎么快速适配。

另外华为机试有些题对输出格式有明确要求,比如输出结果需保留两位小数,或者多答案时按字典序输出。这类要求经常藏在“输出描述”里,不仔细看就会栽跟头。我的习惯是做题前先把输入输出描述圈出来,逐词读一遍,再开始写代码。

4.3 高效的备考顺序和刷题建议

如果你时间紧,我建议按下面这个节奏来:第一周只练输入输出专项和简单模拟题,目标是看到任何输入格式都能在 1 分钟内写出解析代码;第二到第四周刷高频算法专题,包括数组、哈希表、双指针、二分查找、动态规划、图遍历;剩下时间做真题和模拟题,而且一定要在 OJ 上提交完整程序,不要只在本地 IDE 里自嗨。

平时最好维护一个自己的“模板文件”,把快速读入、输出格式化、常见数据结构的操作都封装好,比如这样的结构:

import sys def solve(): data = sys.stdin.read().split() # ... return ans if __name__ == "__main__": result = solve() print(result)

这样每次考试开始后,先把模板敲出来,再进入读题和思考阶段,能省下不少时间。我实测过,在机器上从头敲这段模板只需要 20 秒左右,但能让你面对任何输入格式都心里有底。

5. 这些输入输出的坑,我替你们踩过了

5.1 最常见的五类致命问题

做 OJ 这些年,我见过无数人(包括我自己)在同一批坑里反复摔跤。这里整理五类最常见的致命问题,每一个都能让你的 AC 变成 WA 或者 RE。

第一个是循环读入时崩溃。用 while True + input() 读多行,读到文件末尾没有捕获 EOFError,程序直接抛异常。解决办法很简单,要么用 for line in sys.stdin,要么 while 循环里包 try/except EOFError。

第二个是字符串带了换行符或隐藏字符。有些字符串题目要求输出原字符串,但你没做 strip(),输入行末尾的 \n 就被带进了结果里,导致输出比对不一致。用 .strip() 或者 .rstrip("\n") 都能解决。

第三个是split使用姿势不对。有人写 line.split(' '),但输入里可能有多个连续空格,结果数组中会出现空字符串,转 int 时直接报错。直接用不带参数的 split() 就可以,它会连续处理多个空格和 tab。

第四个是输出混入了调试信息。很多人用 print 打印中间变量,提交时忘了删,结果答案前后多出一堆数字或文字。在线评测系统比对的是完整输出,多一个字符都会判错。

第五个是输出格式不对。比如要求输出一行用空格分隔的数字,你写成每次 print 一个数,等于每个数之间隔了一个换行,自然不正确。

我把这些问题整理成速查表,方便你自查:

现象可能原因解决方案
EOFErrorwhile True + input() 读到末尾改为 for line in sys.stdin
输出莫名多了空行字符串带 \nstrip() 或 rstrip("\n")
列表里有空字符串split(' ') 遇多个空格使用无参 split()
答案前后多出调试信息print 未删除提交前全文搜索 print
数字之间换行而非空格循环 print 单个值用 join 拼接一行输出

5.2 排查思路和自测方法

遇到提交报错,别慌,先用本地模拟输入输出排查。最简单的方法是把样例输入保存到 input.txt,然后用命令行重定向的方式运行:

python3 solution.py < input.txt

然后把输出和题目给的样例输出做对比。如果一致,说明基本逻辑没问题,问题可能出在隐藏数据上;如果不一致,你就能立刻定位到是解析错了还是算法错了。

更多的隐藏数据问题可以用“小规模对拍法”来排查。写一个最简单的暴力解法,再写一个针对随机小数据生成输入的脚本,把两个程序的输出反复对比,一旦不一致,就缩小范围找 bug。这个方法虽然土,但在准备机试时非常有效,因为很多隐蔽错误只有在特定数据下才会暴露。

5.3 我的一点独家心得

除了上面的常规坑,还有几个小技巧是很多人不注意的。第一,转 int 之前先判断字符串是不是数字,可以用 str.isdigit(),不过负数带负号时这个方法不好使,最好直接用 int() 并配合 try/except。第二,如果输出结果需要对浮点数取整,尽量不要依赖 round(),用 format 或 f-string 更可控。第三,读入很大时可以给 sys.stdin 设置缓冲区,但默认缓冲已经够用,不需要额外折腾。

我还会在本地模板里提前写好一个 DEBUG 开关:

import sys DEBUG = False def solve(): if DEBUG: sys.stdin = open("input.txt", "r", encoding="utf-8") data = sys.stdin.read().split() # ... if __name__ == "__main__": print(solve())

平时把 DEBUG 改成 True,直接从 input.txt 读数据;提交时改成 False,就恢复正常标准输入。这样既不会忘删文件读取代码,又能快速切换,对我来说是效率最高的方式。

6. 本地调试与提速实操

6.1 用文件重定向模拟在线评测

在正式机试前,我强烈建议你养成“本地文件模拟评测”的习惯。具体做法是:先创建一个 input.txt,把题目给的样例输入原样放进去,然后在命令行下运行:

python3 solution.py < input.txt

这样你的程序就会从 input.txt 读取输入,而不是等待键盘输入。这一步看起来简单,却能提前发现很多致命问题,比如读入死循环、换行符处理错误、输出格式不对等。我在准备华为机试的那段时间,几乎每道题都会先用这种方式自测一遍,发现问题的效率比单纯盯着 IDE 高很多。

如果你用的 IDE 不便于命令行操作,还可以在代码里把 sys.stdin 替换成文件对象。记住,无论是重定向还是文件替换,提交到 OJ 前都一定要改回来,否则程序会找不到文件而报错。

6.2 把解析和算法逻辑分开写

很多新手喜欢把读入、解析、计算、输出全揉在一起,结果代码一长就乱,出 bug 后找不到问题。我推荐一个更清晰的代码组织方式:把“读入数据”和“算法逻辑”拆成两个部分,中间通过参数传递。比如:

import sys def solve(nums): # 只关注算法逻辑,nums 是已经解析好的数据 return sum(nums) def main(): data = sys.stdin.read().split() nums = list(map(int, data[1:])) # 假设第一个数是数组长度 print(solve(nums)) if __name__ == "__main__": main()

这样写的好处有三个:第一,算法函数可以独立测试,直接调用 solve([1, 2, 3]) 就能看结果;第二,以后遇到同类输入格式,直接复制 main 函数里的解析代码;第三,如果题目改成核心代码模式,你只需要把 solve 函数剥离出去,适配非常快。

6.3 读取性能实测对比

最后说说性能。很多人以为 input() 和 sys.stdin 只在“很极端”的数据下才有区别,其实这距离机试并不远。我做过一个小实验:生成 100 万行数字,每一行一个数,分别用 input() 循环读和 sys.stdin.read() 全量读,前者耗时差不多是后者的 3 倍以上。在内存不紧张的情况下,sys.stdin.read() 永远是更快的那一个。

不过也要提醒,sys.stdin.read() 会把全部输入读进内存,如果输入有几百 MB(机试一般不会这么大),才需要担心内存问题。正常机试数据规模下,这个方案完全没问题。为了保险,我习惯在读入后立刻把大字符串转成列表,让解析尽早完成,避免后续逻辑里反复做字符串操作。

想亲手测性能的话,可以用 time 模块简单计时:

import sys import time start = time.time() data = sys.stdin.read().split() print(f"read cost: {time.time() - start:.3f}s")

建议你在本地跑一次这个对比,亲身体会一下差距,之后写代码就会下意识选择更高效的读入方式。

最后说点个人感受。我最早在牛客网刷题的时候,也是一顿操作猛如虎,提交一看各种 0 分,后来才发现全是输入输出的问题,反而算法思路本身没有错。那之后我花了整整两天时间,把常见输入格式的模板全部背下来,又用本地文件和重定向的方法反复自测,后面再参加华为机试,就再没在 IO 上栽过跟头。输入输出这东西,说难也不难,但它就像游泳前的热身,不做好,下水就容易抽筋。花点时间把模板和习惯练好,你在考场上就能把全部脑力留给真正的算法题,这笔时间花得非常值。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询