Checker

C 对数器(对拍器)

对数器是竞赛中十分重要的技能
对数器是将两份代码的输出进行比较,如果不一样则输出两种代码的输出,是一种十分高效方便的工具
配合oi.wiki食用更佳
oi.wiki-命令行

目录

随机数生成器

整数范围内的随机数生成器(1~100)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {
srand(time(NULL)); // 设置随机数种子

int min = 1;
int max = 100;

// 生成一个1到100之间的随机数
int random_number = min + rand() % (max - min + 1);

printf("Random number between 1 and 100: %d\n", random_number);
return 0;
}

浮点范围内的随机数生成器

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {
srand(time(NULL)); // 设置随机数种子

// 生成一个[0.0, 1.0)范围内的浮动随机数
double random_double = (double)rand() / RAND_MAX;

printf("Random double between 0.0 and 1.0: %f\n", random_double);
return 0;
}

实际情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

int main() {
srand(time(NULL)); // 设置随机数种子

// 生成 T(循环次数)
int T = rand() % 10 + 1; // 生成 1 到 10 之间的循环次数
cout << "T = " << T << endl;

// 循环 T 次
while (T--) {
// 生成 n 和 m
int n = rand() % 10 + 1; // 生成 1 到 10 之间的数组大小
int m = rand() % 100 + 1; // 生成 1 到 100 之间的 m 值

cout << "n = " << n << ", m = " << m << endl;

// 生成数组 a[n]
int a[n];
for (int i = 0; i < n; i++) {
a[i] = rand() % 100 + 1; // 生成 1 到 100 之间的数组元素
cout << a[i] << " "; // 输出数组元素
}
cout << endl; // 输出换行
}

return 0;
}

C语言的代码

click here
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void run_program(const char *program, const char *input, char **output) {
size_t buffer_size = 1024;
size_t output_size = buffer_size;
*output = (char *)malloc(output_size);
if (*output == NULL) {
perror("malloc failed");
exit(EXIT_FAILURE);
}

char command[buffer_size];
snprintf(command, sizeof(command), "echo %s | %s", input, program);
FILE *fp = popen(command, "r");
if (fp == NULL) {
perror("open filed failed");
exit(EXIT_FAILURE);
}

size_t len = 0;
while (fgets(*output + len, buffer_size, fp) != NULL) {
len += strlen(*output + len);
if (len + buffer_size > output_size) {
output_size *= 2;
*output = (char *)realloc(*output, output_size);//realloc函数,动态扩充或者缩小数组长度,也就是动态改变数组分配的内存
if (*output == NULL) {
perror("realloc failed");
exit(EXIT_FAILURE);
}
}
}
pclose(fp);
}

int main() {
FILE *input_file = fopen("input.txt", "r");
if (input_file == NULL) {
perror("fopen input.txt failed");
return EXIT_FAILURE;
}

fseek(input_file, 0, SEEK_END);
long input_size = ftell(input_file);
fseek(input_file, 0, SEEK_SET);

char *input = (char *)malloc(input_size + 1);
if (input == NULL) {
perror("malloc failed");
return EXIT_FAILURE;
}

fread(input, 1, input_size, input_file);
input[input_size] = '\0';
fclose(input_file);

char *output1 = NULL;
char *output2 = NULL;

run_program("./program1", input, &output1);
run_program("./program2", input, &output2);

FILE *output_file = fopen("output.txt", "w");
if (output_file == NULL) {
perror("fopen output.txt failed");
return EXIT_FAILURE;
}

if (strcmp(output1, output2) == 0) {
fprintf(output_file, "Outputs are identical.\n");
} else {
fprintf(output_file, "Outputs differ.\n");
fprintf(output_file, "Output from program 1:\n%s\n", output1);
fprintf(output_file, "Output from program 2:\n%s\n", output2);
}

fclose(output_file);
free(input);
free(output1);
free(output2);

return 0;
}

C++的代码

click here
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <cstdlib>

using namespace std;

void run_program(const string &program, const string &input, string &output) {
string command = "echo " + input + " | " + program;
FILE *fp = popen(command.c_str(), "r");
if (fp == nullptr) {
perror("open failed");
exit(EXIT_FAILURE);
}

char buffer[1024];
while (fgets(buffer, sizeof(buffer), fp) != nullptr) {
output += buffer;
}
pclose(fp);
}

int main() {
// Read input from the file
ifstream input_file("input.txt");
if (!input_file) {
perror("fopen input.txt failed");
return EXIT_FAILURE;
}

stringstream buffer;
buffer << input_file.rdbuf();
string input = buffer.str();

input_file.close();

// Variables to hold the output from both programs
string output1, output2;

// Run both programs
run_program("./program1", input, output1);
run_program("./program2", input, output2);

// Write output to the output file
ofstream output_file("output.txt");
if (!output_file) {
perror("fopen output.txt failed");
return EXIT_FAILURE;
}

if (output1 == output2) {
output_file << "Outputs are identical.\n";
} else {
output_file << "Outputs differ.\n";
output_file << "Output from program 1:\n" << output1 << "\n";
output_file << "Output from program 2:\n" << output2 << "\n";
}

output_file.close();

return 0;
}

程序概述

该程序首先从一个名为 input.txt 的文件中读取输入内容。然后,它将输入传递给两个外部程序 program1program2,分别捕获它们的输出。最后,程序将这两个输出进行比较,并在 output.txt 文件中记录比较结果。如果两个输出相同,记录 “Outputs are identical”;如果不同,则记录 “Outputs differ” 并输出两个程序的具体输出内容。

库函数和特殊变量介绍

size_t

size_t 是一种无符号整数类型,通常用于表示对象的大小或数组的索引,尤其是在涉及内存分配、内存操作和数组访问时。它被定义在<stddef.h><stdio.h>等标准头文件中

  • 无符号类型:size_t 是无符号整数类型,意味着它只能表示正整数(包括零)
  • 平台相关大小:size_t 的大小依赖于编译平台。它通常被定义为能够存储平台上最大对象大小的类型:
    • 在 32 位系统中,size_t 通常是 32 位(4 字节)整数。
    • 在 64 位系统中,size_t通常是 64 位(8 字节)整数。
  • 常用于数组索引和内存大小:由于size_t 可以表示较大的值,它常用于表示内存块的大小、数组的索引或其他需要表示大小或长度的场景。
  • 内存分配函数:如 malloccallocrealloc 等函数的返回值类型就是 size_t,用于表示分配的内存大小。
1
2
size_t size = 10;
int *arr = (int *)malloc(size * sizeof(int)); // 用size_t来表示内存大小

exit(EXIT_FAILURE)

  • exit() 是一个标准库函数,定义在<stdlib.h>中,用于终止程序的执行
  • 1EXIT_FAILURE 1是一个宏,通常在1 <stdlib.h> 1中定义,用来表示程序非正常退出的状态码。其值通常是 1,表示错误或异常状态
1
exit(int status);
  • 参数 status:传递给exit()status参数是程序的退出状态码。通常情况下,操作系统和其他程序可以检查该状态码,以了解程序是成功结束还是由于某些错误退出。
    • EXIT_SUCCESS(通常是 0):表示程序正常结束。
    • EXIT_FAILURE(通常是 1):表示程序出现错误并异常结束。

snprintf(command, sizeof(command), “echo %s | %s”, input, program)

snprintf函数

1
int snprintf(char *str, size_t size, const char *format, ...);
  • str:目标缓冲区,snprintf 会将格式化的字符串写入这个缓冲区。
  • size:目标缓冲区的大小,snprintf 会确保不会超过这个大小,防止写入超出缓冲区的内容。
  • format:格式化字符串,它定义了如何格式化后续传入的变量。
  • ...:根据格式化字符串传入的参数。
1
snprintf(command, sizeof(command), "echo %s | %s", input, program);
  • command:这是目标缓冲区,用于存储最终格式化后的字符串。它是一个字符数组,通常需要足够大以容纳整个命令字符串。

  • sizeof(command):返回command数组的大小,表示snprintf最多可以写入多少字符到command中。它确保不会出现缓冲区溢出问题。

  • "echo %s | %s":这是格式化字符串,用于指定最终生成的字符串的格式。

  • %s 是一个占位符,用来插入后续传入的字符串参数。第一个%s用于插入 input,第二个%s用于插入 program

  • input program:这两个是将要插入格式化字符串中的实际参数。

  • input 是输入字符串,它会作为echo命令的参数,传递给外部命令。
    program 是外部程序的名称或路径,它将作为管道(|)命令的后续部分,用于处理 input

语句的含义

1
"echo <input> | <program>"
  • echo <input>:将 input 字符串打印到标准输出。
  • <program>:通过管道符(|)将 echo 输出的内容作为输入传递给 program 程序

popen

popen是 C 语言中用于创建一个进程并打开一个管道(pipe)来执行外部命令的函数,它定义在 <stdio.h>头文件中。通过popen,你可以执行外部命令并通过管道读取命令的输出,或者向命令的标准输入传递数据

1
2
FILE *popen(const char *command, const char *mode);
FILE *fp = popen("ls -l", "r"); // 执行 'ls -l' 命令并以只读模式打开管道
  • command:这是要执行的命令字符串,类似于在命令行中输入的命令。command可以是任何可以在终端中执行的有效命令或程序
  • mode:表示打开管道的模式,指定你是从管道读取输出,还是向管道写入输入
  • "r":以只读模式打开管道,用来读取命令的标准输出。
  • "w":以写模式打开管道,用来将数据写入命令的标准输入。

popen and fopen

  • fopen:用于打开一个文件并返回一个文件指针(FILE *)。这个文件可以是一个常规的磁盘文件,或者是一个设备文件。你可以用fopen打开文件进行读取、写入、追加等操作。
  • popen:用于执行一个外部命令,并通过管道与该命令的标准输入、标准输出或标准错误进行交互。它创建一个进程,执行指定的命令,并将输出通过管道返回,或者将输入通过管道传递给命令。popen 主要用于与外部程序进行交互,而不是直接访问文件系统中的文件。

fprintf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>

int main() {
FILE *file = fopen("output.txt", "w");
if (file == NULL) {
perror("fopen failed");
return 1;
}

int age = 25;
float height = 5.9;
const char *name = "John Doe";

fprintf(file, "Name: %s\n", name);
fprintf(file, "Age: %d\n", age);
fprintf(file, "Height: %.2f meters\n", height);

fclose(file);
return 0;
}

此时output.txt的内容是

1
2
3
Name: John Doe
Age: 25
Height: 5.90 meters

主要函数介绍

run_program 函数

1
void run_program(const char *program, const char *input, char **output);

run_program 函数的作用是执行指定的外部程序,并将标准输入 (input) 传递给它,最终捕获该程序的标准输出。

参数

  • program: 外部程序的路径或名称。
  • input: 将作为输入传递给外部程序的字符串。
  • output: 捕获外部程序输出的指针。该指针会指向一个动态分配的内存区域,用来存储程序的输出。

功能

  1. 内存分配:首先分配 1024 字节的内存用于存储输出,如果输出的内容超过该大小,会动态扩展内存。
  2. 执行外部命令:使用 popen 函数执行外部程序,并通过管道将输入传递给该程序。
  3. 捕获输出:使用 fgets 逐行读取程序的输出。如果输出超过了初始分配的缓冲区大小,动态扩展缓冲区。
  4. 资源清理:在完成输出捕获后,关闭文件流并释放分配的内存。

实际例子

假设我们有两个外部程序 program1program2,它们分别输出一些内容。调用 run_program 时,我们将输入传递给它们并捕获输出:

1
2
3
4
5
6
7
8
9
char *output1 = NULL;
run_program("./program1", "Hello, World!", &output1);
printf("Program 1 Output: %s\n", output1);
free(output1);

char *output2 = NULL;
run_program("./program2", "Hello, World!", &output2);
printf("Program 2 Output: %s\n", output2);
free(output2);

main 函数

unfold
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
int main() {
// 从文件读取输入内容
FILE *input_file = fopen("input.txt", "r");
if (input_file == NULL) {
perror("fopen input.txt failed");
return EXIT_FAILURE;
}

fseek(input_file, 0, SEEK_END);
long input_size = ftell(input_file);
fseek(input_file, 0, SEEK_SET);

char *input = (char *)malloc(input_size + 1);
if (input == NULL) {
perror("malloc failed");
return EXIT_FAILURE;
}

fread(input, 1, input_size, input_file);
input[input_size] = '\0';
fclose(input_file);

// 执行外部程序并捕获输出
char *output1 = NULL;
char *output2 = NULL;

run_program("./program1", input, &output1);
run_program("./program2", input, &output2);

// 比较输出并写入到文件
FILE *output_file = fopen("output.txt", "w");
if (output_file == NULL) {
perror("fopen output.txt failed");
return EXIT_FAILURE;
}

if (strcmp(output1, output2) == 0) {
fprintf(output_file, "Outputs are identical.\n");
} else {
fprintf(output_file, "Outputs differ.\n");
fprintf(output_file, "Output from program 1:\n%s\n", output1);
fprintf(output_file, "Output from program 2:\n%s\n", output2);
}

fclose(output_file);

// 清理内存
free(input);
free(output1);
free(output2);

return 0;
}

功能

  1. 读取输入文件main 函数首先打开名为 input.txt 的文件,并读取其内容。文件内容存储在动态分配的 input 字符串中。
  2. 调用外部程序:它调用 run_program 两次,分别将输入传递给 program1program2,并捕获它们的输出。
  3. 比较输出:程序使用 strcmp 比较两个程序的输出。如果相同,输出 “Outputs are identical”;否则,输出 “Outputs differ” 并打印两个程序的输出内容。
  4. 释放资源:程序在结束时释放动态分配的内存。

实际例子

假设 input.txt 文件内容如下:

1
Test input data

如果 program1program2 分别输出如下:

  • program1 输出:Test input data processed by program 1
  • program2 输出:Test input data processed by program 2

那么,程序的 output.txt 文件将包含以下内容:

1
2
3
4
5
6
Outputs differ.
Output from program 1:
Test input data processed by program 1

Output from program 2:
Test input data processed by program 2

实战

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

void gendata() {
char command[256];
sprintf(command, "%s > %s", "gendata.exe", "input.txt");
system(command);
}

void runTest(const char *program, const char *input, const char *output) {
char command[256];
sprintf(command, "%s < %s > %s", program, input, output);
system(command);
}

int cmp(const char *file1, const char *file2) {
FILE *f1 = fopen(file1, "r");
FILE *f2 = fopen(file2, "r");
if (!f1 || !f2) {
perror("failed to open file");
return -1;
}
char line1[256], line2[256];
while (fgets(line1, 256, f1) && fgets(line2, 256, f2)) {
if (strcmp(line1, line2) != 0) {
fclose(f1);
fclose(f2);
return 0;
}
}
fclose(f1);
fclose(f2);
return 1;
}

void printInput() {
FILE *inputFile = fopen("input.txt", "r");
if (!inputFile) {
perror("failed to open input file");
return;
}
char line[256];
while (fgets(line, 256, inputFile)) {
printf("%s", line);
}
fclose(inputFile);
}

int main() {
srand(time(NULL));
int T = rand() % 200 + 1, flag = 1;
for (int i = 0; i < T; i++) {
gendata();
runTest("code1.exe", "input.txt", "output1.txt");
runTest("code2.exe", "input.txt", "output2.txt");
if (!cmp("output1.txt", "output2.txt")) {
printf("Wrong Answer\n");
printf("Input data:\n");
printInput();
flag = 0;
return 0;
}
}
if (flag) printf("Accepted\n");
return 0;
}

Checker
http://example.com/2024/11/14/Checker/
Author
Anfsity
Posted on
November 14, 2024
Licensed under