用VSCode终端实现重定向比较程序输出和正确输出
在刷 OJ 题目或者进行编程考试或比赛时,经常需要对编写好的程序进行测试,即运行编写好的程序,输入样例输入或者自己编写的输入数据,查看程序输出结果和样例输出或者正确输出是否一致。这种方法有很多弊端,当有多组输入数据或程序运行结果多次错误时,需要多次复制粘贴输入数据,这个过程非常繁琐而且浪费时间;用肉眼检查程序输出和正确输出是否一致很容易出错,尤其是当输出数据非常多时。所以,我在这篇博客里介绍一下通过输入输出重定向和 windows 批处理文件比较程序输出和正确输出的方法。由于 VSCode 能够编写任意格式的文件且自带终端,本博客基于 VSCode 编写代码。如果你没有安装 VSCode,可以参考挑把趁手的兵器——VSCode 配置 C/C++学习环境(小白向)安装 VSCode 并配置 C/C++环境。当然使用你常用的编辑器/IDE,并利用 powershell 或 DOS 窗口运行程序 windows 脚本也是可以的。
概念介绍
- 输入输出重定向:最常见的输入输出是标准输入输出,即读键盘输入、写屏幕。但当我们希望在文件中准备好输入数据,将输出或错误信息输入到另一个文件中时,就需要使用重定向。本博客介绍的方法就是将输入数据保存在一个 input.txt 文件中,运行程序时,让程序从 input.txt 文件读取数据,将程序输出数据保存在另一个 output.txt 文件中,从而就避免了多次复制粘贴输入数据的繁琐步骤。
- windows 批处理文件:批处理,顾名思义就是进行批量的处理。批处理文件是扩展名为.bat 或.cmd 的文本文件,包含一条或多条命令,由 DOS 或 Windows 系统内嵌的命令解释器来解释运行。本博客提出的方法使用的是 windows 批处理文件中的比较文件差异的 fc 命令。
比较程序输出和样例输出
算法题目通常都会给出多组样例输入和样例输出。用肉眼检查程序输出和样例输出是否一致很容易出错,我们可以利用 windows 脚本运行我们编写的程序并比较程序输出和样例输出是否一致。
我们可以以一个读取两个 int 数据,输出这两个数据之和的简单程序作为例子,这个程序的 C++程序代码如下:
#include <bits/stdc++.h>
using namespace std;
int main() {
int a, b;
cin >> a >> b;
cout << a + b << "\n";
return 0;
}
不妨将程序其命名为 test.cpp,我们不妨在 VSCode 中新建这样的 cpp 文件,如下图所示:
新建三个 txt 文件 input.txt、output.txt、correct.txt,它们的作用分别为:
- input.txt:用于存放输入数据
- correct.txt:用于存放正确的输出数据
- output.txt:用于存放程序输出数据,这个文件不需要新建和删除,运行 windows 批处理文件后会自动生成
我们不妨在 input.txt 文件中写入1 2
作为输入数据,在 correct.txt 写入3
作为正确的输出数据,如下所示:
新建一个 windows 脚本,不妨命名为run.bat
,里面写入代码:
g++ test.cpp -std=c++17 -o test %编译test.cpp,生成test.exe%
test < input.txt >output.txt %执行test.exe文件,从input.txt文件读取输入,将程序结果输出到output.txt文件%
fc output.txt correct.txt %比较output.txt文件和correct.txt文件是否相同%
如果你安装了Code Runner
插件,可以直接右键->run code
执行。如果没有安装,那么启动终端,并输入命令.\run.bat
,回车即可。
如果有多组输入,多组输出,怎么办呢?我们可以修改一下程序 test.cpp:
#include <bits/stdc++.h>
using namespace std;
int main() {
int a, b;
int t = 1; //数据组数
cin >> t;
while (t--) {
cin >> a >> b;
cout << a + b << "\n";
}
return 0;
}
增加一组输入-1 -2
,增加一组输出-3
,我们可以这样填写 input.txt 文件和 correct.txt 文件:
然后运行run.bat
脚本即可比较多组数据的输出。
如果题目每个测试点只有一组数据,在提交时只需要注释掉第 6 行代码cin >> t;
即可。
程序对拍
有时程序能够通过样例,但是提交之后评测系统总会报错,也就是说程序中有 bug,这时就需要通过程序对拍来找到一组使程序错误的数据。要完成程序对拍,我们需要 3 个 cpp 文件。
- 我们自己编写的有错误的程序,不妨命名为
test.cpp
。 - 能够正确解题的程序,不妨命名为
correct.cpp
。如果是平时练习,我们通常可以在网上搜索到这个题目正确的解题代码;如果是比赛期间,我们只能编写一个暴力但正确的程序。 - 能够产生题目要求的输入数据的程序,不妨命名为
data.cpp
。
我们还是以一个读取两个 int 数据,输出这两个数据之和的简单程序作为例子。假设它的输入数据是不超过\([-10^6,10^6]\)之间的两个整数,data.cpp
可以这样写:
#include <bits/stdc++.h>
using namespace std;
int main() {
uniform_int_distribution<int> u(-1e6, 1e6); //设置随机数的范围和分布
default_random_engine e(time(0)); //设置随机数引擎
cout << u(e) << " " << u(e) << "\n"; //输出随机数
return 0;
}
correct.cpp
可以这样写:
#include <stdio.h>
int main() {
int a, b;
scanf("%d%d", &a, &b);
printf("%d", a + b);
}
假设test.cpp
为:
#include <bits/stdc++.h>
using namespace std;
int main() {
int a, b;
cin >> a >> b;
cout << a + b << "\n";
return 0;
}
编写一个compare.bat
脚本:
g++ data.cpp -std=c++17 -o data
g++ test.cpp -std=c++17 -o test
g++ correct.cpp -std=c++17 -o correct
@echo off
:loop
data > input.txt
test < input.txt > output.txt
correct < input.txt > correct.txt
fc output.txt correct.txt
if not errorlevel 1 goto loop
pause
goto loop
compare.bat
脚本将data.cpp
的输出写入到input.txt
文件,将correct.cpp
的输出写入到correct.txt
文件,将test.cpp
的输出写入到output.txt
文件。这个脚本是一个死循环,它不会不断产生新的随机输入,并比较test.cpp
和correct.cpp
的输出,直到两个输出不一致才会停下来。显然test.cpp
是正确的程序,因此脚本将无限执行下去:
如果我们将test.cpp
修改成这样:
#include <bits/stdc++.h>
using namespace std;
int main() {
int a, b;
cin >> a >> b;
cout << a << "\n";
return 0;
}
显然,这是一个错误的程序,执行compare.bat
脚本会得到这样的结果:
它会在一组让test.cpp
产生错误的数据处停止。这样一个错误数据可以方便我们后续的 debug。
注意,由于产生输入数据的程序只是一个简单的随机数程序,并不能保证一定能得到使程序产生错误的数据,所以数据对拍只能作为帮助我们 debug 的一种方法,但并不是总能奏效。