对拍
1. 背景
做题目的时候,如果这个题目的只有样例通过,或只拿到部分的时候,我们需要一个数据,这个数据可以过标程或暴力但不能过过我们的代码,以此来找到我们代码中错误
2. 基础知识
diff
命令的使用
diff
命令的是linux自带的命令,它可以比较两个文本文件的不同
首先创建两个文件1.txt
,2.txt
,内容如下
1
2
3
4
5
2
3
4
5
1
2
2
4
5
2
3
4
5
ctrl + alt + t,打开termial,输入下面的内容
diff 1.txt 2.txt
那么这个输出格式是什么意思呢
得到结果
3c3
< 3
---
> 2
2
3
4
c
表示change
,3c3
表示1.txt
的第3行和2.txt
的第3行对比,有改变
具体解释见 diff命令输出格式解读_车子 chezi的博客-CSDN博客_diff 输出
你再尝试一下其它命令
# q -> quiet,安静模式,相同,什么也不输出
diff -q 1.txt 1.txt
# q -> quiet,安静模式,不相同,输出指明不同
diff -q 1.txt 2.txt
# y -> side by slide,并排输出
diff -y 1.txt 2.txt
# 指定宽度为30
diff -W 30 -y 1.txt 2.txt
# 只输出不同的行
diff --suppress-common-lines -y 1.txt 2.txt
2
3
4
5
6
7
8
9
10
11
12
13
14
15
终极杀招vimdiff
vimdiff 1.txt 2.txt
vimdiff
具有下面的优点
- 命令简单
- 显示完美
- 相同的行会折叠
- 不相同的行会高亮
3. diff 返回的值
我们知道,我们写的c++代表的main函数都会return一个整数,同样diff命令在运行结束 后也会返回一个值. 那么怎么得到这个值呢?
diff 1.txt 1.txt
echo $?
2
会输出0
,表示diff的两个文件一样
4. diff 命令的与||
的拼接
diff -q 1.txt 1.txt || echo "yes"
我们知道 在c++,里||
是逻辑或运行符号,那么这里是什么意思呢?
比较反直觉的是bash是如何判断一个程序运行的是否正确,如下
- 一个命令运行的返回值是0,bash认为是true,这个程序运行正确
- 一个命令运行的返回值是非0,bash认为是false,这个程序运行不正确
||
的运算规则和c++里一样,A || B
试一试下面的命令
# 不会输出yes
true || echo "yes"
# 会输出yes
false || echo "yes"
2
3
4
5
- 如果A是true,不会执行B,因为整体
A || B
一定是true - 如果A是false,会执行B
The right side of && will only be evaluated if the exit status of the left side is zero (i.e. true). || is the opposite: it will evaluate the right side only if the left side exit status is non-zero (i.e. false).
结论 只有diff执行失败时才会执行,echo yes
5. 创建shell对拍脚本
#!/bin/env bash
rand=myrand #假如你的数据生成的程序名 : myrand
my=1 #假如你的写的程序名 : 1
right=2 #假如一个一定正确的程序名 : 2
for i in {1..1000};do # 循环1000次
echo $i #输出执行的次数
./$rand > rand.in # 生成数据
./$my < rand.in > my.out # 生成你的答案
./$right < rand.in > right.out # 正确的答案
diff -q my.out right.out || exit 1 # 如果出错就退出
done
echo "all right"
2
3
4
5
6
7
8
9
10
11
12
# 复制后,下面的命令快速创建一个文件:
xsel -o -b > 1.sh
# 运行权限
chmod +x 1.sh
# 运行
./1.sh
2
3
4
5
6
7
8
9
10
11
12
当然也可以使用下面这些命令之一快速下载创建1.sh
wget -O 1.sh https://rbook.roj.ac.cn/appendix/utils/compare/dp1.sh
从国内的gitee下载
wget -O 1.sh https://gitee.com/Rainboy/RBook/raw/master/appendix/utils/compare/dp1.sh
从国外的github下载
wget -O 1.sh https://github.com/rbookr/RBook/raw/master/appendix/utils/compare/dp1.sh
不要忘记chmod +x 1.sh
改进1: 能不能不要每一次写个代码就创建一次1.sh
呢?
那你要做到:
- 把脚本放到
PATH
目录里,可以全局使用这个命令 - 使用脚本可以得到参数
#!/bin/env bash
my=$1 # 第一个参数指明: 你的程序的名字
right=$2 # 第二个参数指明: 对的程序的名字
rand=$3 # 第三个参数指明: 随机数据生成程序
for i in {1..1000};do
echo $i
./$rand > rand.in
./$my < rand.in > my.out
./$right < rand.in > right.out
diff -q my.out right.out || exit 1
done
echo "all right"
2
3
4
5
6
7
8
9
10
11
12
# 复制到文件里
xsel -o -b > dp.sh
# 给运行的权限
chmod +x dp.sh
# 放入到这个目录就可以执行了
sudo mv dp.sh /usr/bin
2
3
4
5
6
7
8
当然也可以使用下面这些命令之一快速下载创建dp.sh
wget -O dp.sh https://rbook.roj.ac.cn/appendix/utils/compare/dp2.sh
从国内的gitee下载
wget -O dp.sh https://gitee.com/Rainboy/RBook/raw/master/appendix/utils/compare/dp2.sh
从国外的github下载
wget -O dp.sh https://github.com/rbookr/RBook/raw/master/appendix/utils/compare/dp2.sh
不要忘记chmod +x dp.sh
6 c++对拍程序
Q:因为我不熟悉bash
脚本的编写, ,我比赛时无法记住上面的脚本,怎么办呢?
A:使用如下的c++代码
#include <iostream>
using namespace std;
int main(){
for(int i=1;i<=1000;++i){
cout << i << "\n";
// system 相当于手动执行一条命令
system("./rand > rand.out");
system("./my < rand.out > my.out");
system("./right < rand.out > right.out");
if( system("diff -q my.out right.out") != 0){
cout << "error \n";
return 1;
}
}
cout << "all right\n";
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19