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

Python脚本输出含 致自动化测试断言失败求助

解决Python脚本输出换行符不一致导致断言失败问题

问题背景

我写了一个Python脚本,功能是读取英拉字典文件(英文单词对应多个拉丁词),生成拉英字典并输出到控制台,要求合并同一拉丁词的英文释义并去重。但自动化测试时发现脚本输出始终带\r(即换行是\r\n),和预期的纯\n换行不符,导致断言失败。试过修改PyCharm换行符设置、在代码里替换\r,都没用,甚至最小复现代码里用\n拼接输出,结果还是\r\n。

原脚本代码

import sys
for filename in sys.argv[1:]:
    with open(filename, 'r', encoding='utf-8') as f:
        res_dict = {}
        for s in f.readlines():
            cur_word = s.split()[0]
            translations = s.strip().replace(',', '').split()[2:]
            for i in translations:
                if i in res_dict:
                    res_dict[i].append(cur_word)
                else:
                    res_dict.setdefault(i, [cur_word])
    res = []
    for k, v in sorted(res_dict.items()):
        res.append(k + ' - ' + ', '.join(v))
    print('\n'.join(res).replace('\r\n', ''))

自动化测试代码

def test_from_file(test_input_file, expected_output_file):
    """
    验证脚本输出正确性的测试函数
    测试输入文件来自'test/resources/task3'目录:
    - test_input_1.txt
    - test_input_2.txt
    """
    result = subprocess.run(
        ["python", os.path.join(SOLUTION_FOLDER_PATH, "task3.py"), test_input_file],
        stdout=subprocess.PIPE,
    )
    student_output = result.stdout.decode().strip()

    with open(expected_output_file, "r") as expected_output_file:
        expected_output_content = expected_output_file.read().strip()

    assert student_output == expected_output_content

最小复现代码

import sys
for filename in sys.argv[1:]:
    words = ['Hello, ', 'World!']
    print('\n'.join(words))

断言错误示例

E       AssertionError: assert 'baca - fruit\r\nbacca - fruit\r\nmalum - apple, punishment\r\nmulta - punishment\r\npomum - apple\r\npopula - apple\r\npopum - fruit' == 'baca - fruit\nbacca - fruit\nmalum - apple, punishment\nmulta - punishment\npomum - apple\npopula - apple\npopum - fruit'

问题原因

Windows系统下,Python的print()函数默认会把\n替换成系统默认换行符\r\n——这是因为stdout默认是文本模式,会自动做跨平台换行符转换。哪怕你用\n拼接字符串,print()输出时还是会被强制替换成\r\n。

解决方案

方法1:用sys.stdout.write()替代print()

write()不会自动添加换行符,也不会转换换行符,直接输出拼接好的内容:

# 替换原脚本中的print语句
sys.stdout.write('\n'.join(res) + '\n')

方法2:强制修改stdout的换行模式

在脚本开头添加代码,强制stdout使用\n作为换行符,关闭自动转换:

import sys
# 重置stdout为指定换行模式
sys.stdout = open(sys.stdout.fileno(), mode='w', encoding='utf-8', newline='\n')

方法3:全局替换所有\r字符

不管\r来自哪里,最后统一清除:

output = '\n'.join(res)
print(output.replace('\r', ''))

(注意:原代码中replace('\r\n', '')只会替换完整的\r\n,但print()自动添加的是单独的\r,所以直接替换所有\r才有效)

方法4:在测试代码中统一处理换行符

如果不想修改脚本,也可以在测试时把两边的换行符统一成同一种格式:

# 处理学生输出的换行符
student_output = result.stdout.decode().strip().replace('\r\n', '\n').replace('\r', '\n')
# 处理预期输出的换行符
expected_output_content = expected_output_file.read().strip().replace('\r\n', '\n').replace('\r', '\n')

修改后的完整脚本(含多文件合并+去重修复)

原脚本还有个隐藏问题:每次处理新文件都会重置res_dict,导致无法合并多个文件的内容。以下是修复后的完整代码:

import sys

def main():
    res_dict = {}
    for filename in sys.argv[1:]:
        with open(filename, 'r', encoding='utf-8') as f:
            for line in f.readlines():
                line = line.strip()
                if not line:
                    continue
                # 处理行内逗号并分割
                parts = line.replace(',', '').split()
                if len(parts) < 3:
                    continue
                eng_word = parts[0]
                latin_words = parts[2:]
                for latin_word in latin_words:
                    # 去重添加英文释义
                    if latin_word not in res_dict:
                        res_dict[latin_word] = []
                    if eng_word not in res_dict[latin_word]:
                        res_dict[latin_word].append(eng_word)
    # 生成输出内容
    output_lines = []
    for latin, eng_list in sorted(res_dict.items()):
        output_lines.append(f"{latin} - {', '.join(eng_list)}")
    # 输出到控制台
    sys.stdout.write('\n'.join(output_lines) + '\n')

if __name__ == "__main__":
    main()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 04:37:34