Logo wfirstzhang 的博客

博客

【Repo】一个可以部分替代 windows fc / linux diff 的程序

...
wfirstzhang
2025-10-11 09:00:16
密码是111111

tokencmp

#include <cassert>
#include <cstdio>
#include <fstream>
#include <string>

int main(int argc, char** argv) {
    using namespace std;
    assert(argc == 3);
    ifstream in1(argv[1]);
    ifstream in2(argv[2]);
    string token1, token2;
    int cnt1 = 0, cnt2 = 0;
    while(true)
    {
        int tmp = cnt1; 
        cnt1 += bool(in1 >> token1);
        cnt2 += bool(in2 >> token2);
        if(cnt1 != cnt2 || tmp == cnt1)
            break;
        if(token1 != token2)
        {
            printf("wrong answer\nWrong answer on token %d, %s read %s, %s read %s.\n",
                cnt1, argv[1], token1.c_str(), argv[2], token2.c_str());
            return 17;
        }
    }
    if(cnt1 < cnt2)
    {
        printf("wrong answer\nFile %s too short.\n", argv[1]);
        return 18; 
    }
    if(cnt2 < cnt1)
    {
        printf("wrong answer\nFile %s too short.\n", argv[2]);
        return 19; 
    }
    printf("ok %d tokens.\n", cnt1); 
    return 0;
}

nlsnel_fcmp

#include <cassert>
#include <cstdio>
#include <fstream>
#include <string>

int main(int argc, char** argv) {
    using namespace std;
    assert(argc == 3);
    ifstream in1(argv[1]);
    ifstream in2(argv[2]);
    string token1, token2;
    int cnt1 = 0, cnt2 = 0;
    while(true)
    {
        int tmp = cnt1; 
        cnt1 += bool(getline(in1, token1));
        cnt2 += bool(getline(in2, token2));
        if(cnt1 != cnt2 || tmp == cnt1)
            break;
        while(!token1.empty() && token1.back() == ' ')
            token1.pop_back();
        while(!token2.empty() && token2.back() == ' ')
            token2.pop_back();
        if(token1 != token2)
        {
            printf("wrong answer\nWrong answer on token %d, %s read %s, %s read %s.\n",
                cnt1, argv[1], token1.c_str(), argv[2], token2.c_str());
            return 17;
        }
    }
    if(cnt1 < cnt2)
    {
        printf("wrong answer\nFile %s too short.\n", argv[1]);
        return 18; 
    }
    if(cnt2 < cnt1)
    {
        printf("wrong answer\nFile %s too short.\n", argv[2]);
        return 19; 
    }
    printf("ok %d tokens.\n", cnt1); 
    return 0;
}

nlsnel_fcmp(testlib

#include "testlib.h"
#include <string>
#include <vector>
#include <sstream>

using namespace std;

bool compareWords(const string& a, const string& b) {
    vector<string> va, vb;
    stringstream sa;

    sa << a;
    string cur;
    while (sa >> cur)
        va.push_back(cur);

    stringstream sb;
    sb << b;
    while (sb >> cur)
        vb.push_back(cur);

    return (va == vb);
}

int main(int argc, char *argv[]) {
    setName("逐行比较 tokens");
    registerTestlibCmd(argc, argv);

    string strAnswer;

    int n = 0;
    while (!ans.eof()) {
        std::string j = ans.readString();

        if (j.empty() && ans.eof())
            break;

        string p = ouf.readString();
        strAnswer = p;

        n++;

        if (!compareWords(j, p))
            quitf(_wa, "第 %d 行不同 - 答案是 '%s',但是读取到 '%s'.", n,
                  compress(j).c_str(), compress(p).c_str());
    }

    if (n == 1)
        quitf(_ok, "一行 '%s'.", compress(strAnswer).c_str());

    quitf(_ok, "共 %d 行.", n);
}

floatcmp

#include <cassert>
#include <cmath>
#include <cstdio>
#include <fstream>

constexpr long double Eps = 1e-2;

int main(int argc, char** argv) {
    using namespace std;
    assert(argc == 3);
    ifstream in1(argv[1]);
    ifstream in2(argv[2]);
    long double token1, token2;
    int cnt1 = 0, cnt2 = 0;
    while(true)
    {
        int tmp = cnt1; 
        cnt1 += bool(in1 >> token1);
        cnt2 += bool(in2 >> token2);
        if(cnt1 != cnt2 || tmp == cnt1)
            break;
        if(abs(token1 - token2) > Eps)
        {
            printf("wrong answer\nWrong answer on token %d, %Lf %Lf.\n",
                cnt1, token1, token2);
            return 17;
        }
    }
    if(cnt1 < cnt2)
    {
        printf("wrong answer\nFile %s too short.\n", argv[1]);
        return 18; 
    }
    if(cnt2 < cnt1)
    {
        printf("wrong answer\nFile %s too short.\n", argv[2]);
        return 19; 
    }
    printf("ok %d tokens.\n", cnt1); 
    return 0;
}

use(windows)

可执行文件名称 输出文件名1 输出文件名2

自动测大样例(builtin_checker)

#include <cassert>
#include <cstdio>
#include <ctime>
#include <fstream>
#include <string>

#ifdef _WIN32
#define COPY_COMMAND "copy"
#define REMOVE_COMMAND "del"
#define RUN_PREFIX ""
#else
#define COPY_COMMAND "cp"
#define REMOVE_COMMAND "rm"
#define RUN_PREFIX "./"
#endif

// 标准输入输出控制标志,文件输入输出请注释掉 
#define STDIO

// 输入后缀 
#define INF_SUF "in"
// 输出后缀 
#define OUF_SUF "out"
// 答案后缀 
#define ANS_SUF "ans"
// 从第几个大样例开始测试 
#define TEST_START 1 

// 默认你可执行文件名等于题目名称 
// argv[1] 是可执行文件名,大样例没有前缀,argv[2]是大样例数目 
// argv[1] 是可执行文件名,大样例前缀是 argv[3],argv[2]是大样例数目 

// 用法两种
// 1. (./)本程序名 题目名称 要测试的大样例数量
// 2. (./)本程序名 题目名称 要测试的大样例数量 大样例前缀 

int builtin_checker(std::string ouf, std::string ans);

int main(int argc, char** argv) {
    using namespace std;
    string title = argv[1], pre = "";
    int cnt = stoi(argv[2]);
    if(argc == 4)
        pre = argv[3];
    for(int i = TEST_START; i < TEST_START + cnt; i++)
    {
        system((COPY_COMMAND " " + pre + to_string(i) + "." INF_SUF  " " + title + "." INF_SUF).c_str());
        auto start = clock();
#ifdef STDIO
        system((RUN_PREFIX + title + " < " + title + "." INF_SUF " >" + title + "." OUF_SUF).c_str());
#else
        system((RUN_PREFIX + title).c_str());
#endif
        double time = double(clock() - start) / CLOCKS_PER_SEC;
        printf("%f s ", time);
        if(builtin_checker(title + "." OUF_SUF, pre + to_string(i) + "." ANS_SUF))
            return 16;
        system((REMOVE_COMMAND " " + title + "." INF_SUF).c_str()); 
    }
    return 0;
}

int builtin_checker(std::string ouf, std::string ans) {
    using namespace std;
    ifstream in1(ouf);
    ifstream in2(ans);
    string token1, token2;
    int cnt1 = 0, cnt2 = 0;
    while(true)
    {
        int tmp = cnt1; 
        cnt1 += bool(getline(in1, token1));
        cnt2 += bool(getline(in2, token2));
        if(cnt1 != cnt2 || tmp == cnt1)
            break;
        while(!token1.empty() && token1.back() == ' ')
            token1.pop_back();
        while(!token2.empty() && token2.back() == ' ')
            token2.pop_back();
        if(token1 != token2)
        {
            printf("wrong answer\nWrong answer on token %d, %s read %s, %s expected %s.\n",
                cnt1, ouf.c_str(), token1.c_str(), ans.c_str(), token2.c_str());
            return 17;
        }
    }
    if(cnt1 < cnt2)
    {
        printf("wrong answer\nFile %s too short.\n", ouf.c_str());
        return 18; 
    }
    if(cnt2 < cnt1)
    {
        printf("wrong answer\nFile %s too long.\n", ouf.c_str());
        return 19; 
    }
    printf("ok %d tokens.\n", cnt1); 
    return 0;
}

定制版

lxn checker

全自动测大样例(linux,需配套chk)

#include <cassert>
#include <chrono>
#include <cstdio>
#include <string>

// 大样例前缀(去掉题目名称)
#define SAMPLE_PREFIX ""
// 工作目录
#define WORK_DIR "cmake-build-debug"
// 运行命令
#define RUN_COMMAND "cd " WORK_DIR "&& ./OI_project_main"
// checker
#define CHECKER "./chk"
// 输入、输出、答案后缀
#define INPUT_SUFFIX ".in"
#define OUTPUT_SUFFIX ".out"
#define ANSWER_SUFFIX ".ans"
// 标准输入输出请启用这个
#define STDIO

// ./run_checks 数据点个数 数据点目录(题目名称)

void exit_fail(int ret) {
    if (ret)
        exit(ret);
}

int main(int argc, char** argv) {
    using namespace std;
    assert(argc == 3);
    int cases = stoi(argv[1]);
    string probtitle = argv[2];
    printf("%s [%d cases]\n", probtitle.c_str(), cases);
    for (int i = 1; i <= cases; i++)
    {
        printf("\nRunning test %d\n", i);
        string ex_prefix = probtitle + "/" SAMPLE_PREFIX + probtitle + to_string(i);
        system(("cp " + ex_prefix + INPUT_SUFFIX " " WORK_DIR "/" + probtitle + INPUT_SUFFIX).c_str());
        auto start = chrono::high_resolution_clock::now();
#ifdef STDIO
        exit_fail(system(
            (RUN_COMMAND " < " + probtitle + INPUT_SUFFIX + " > " + probtitle + OUTPUT_SUFFIX).c_str()));
#else
        exit_fail(system(RUN_COMMAND));
#endif
        auto now = chrono::duration_cast<chrono::milliseconds>(chrono::high_resolution_clock::now() - start);
        printf(" %.3fs ", now.count() / 1e3);
        exit_fail(system((CHECKER " " WORK_DIR "/" + probtitle + OUTPUT_SUFFIX + " " + ex_prefix + ANSWER_SUFFIX).c_str()));
        system(("rm " WORK_DIR "/" + probtitle + ".in").c_str());
        system(("rm " WORK_DIR "/" + probtitle + ".out").c_str());
    }
    return 0;
}

全自动测大样例(linux,配套 3 参数 chk)

#include <cassert>
#include <chrono>
#include <cstdio>
#include <string>

// 大样例前缀(去掉题目名称)
#define SAMPLE_PREFIX ""
// 工作目录
#define WORK_DIR "cmake-build-debug"
// 运行命令
#define RUN_COMMAND "cd " WORK_DIR "&& ./OI_project_main"
// checker
#define CHECKER "./chk"
// 输入、输出、答案后缀
#define INPUT_SUFFIX ".in"
#define OUTPUT_SUFFIX ".out"
#define ANSWER_SUFFIX ".ans"
// 标准输入输出请启用这个
#define STDIO

// ./run_checks 数据点个数 数据点目录(题目名称)

void exit_fail(int ret) {
    if (ret)
        exit(ret);
}

int main(int argc, char** argv) {
    using namespace std;
    assert(argc == 3);
    int cases = stoi(argv[1]);
    string probtitle = argv[2];
    printf("%s [%d cases]\n", probtitle.c_str(), cases);
    for (int i = 1; i <= cases; i++)
    {
        printf("\nRunning test %d\n", i);
        string ex_prefix = probtitle + "/" SAMPLE_PREFIX + probtitle + to_string(i);
        system(("cp " + ex_prefix + INPUT_SUFFIX " " WORK_DIR "/" + probtitle + INPUT_SUFFIX).c_str());
        auto start = chrono::high_resolution_clock::now();
#ifdef STDIO
        exit_fail(system(
            (RUN_COMMAND " < " + probtitle + INPUT_SUFFIX + " > " + probtitle + OUTPUT_SUFFIX).c_str()));
#else
        exit_fail(system(RUN_COMMAND));
#endif
        auto now = chrono::duration_cast<chrono::milliseconds>(chrono::high_resolution_clock::now() - start);
        printf(" %.3fs ", now.count() / 1e3);
        exit_fail(system((CHECKER " " + ex_prefix + INPUT_SUFFIX " " WORK_DIR "/" + probtitle + OUTPUT_SUFFIX + " " + ex_prefix + ANSWER_SUFFIX).c_str()));
        system(("rm " WORK_DIR "/" + probtitle + ".in").c_str());
        system(("rm " WORK_DIR "/" + probtitle + ".out").c_str());
    }
    return 0;
}

全自动测大样例(linux,配套 3 参数 chk,带内存)[AI]

#include <cassert>
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <unistd.h>
#include <vector>
#include <sys/resource.h>
#include <sys/wait.h>

// ====================== 配置 ======================
#define SAMPLE_PREFIX ""
#define WORK_DIR "cmake-build-debug"
#define RUN_BINARY "./OI_project_main"
#define CHECKER "./chk"
#define INPUT_SUFFIX ".in"
#define OUTPUT_SUFFIX ".out"
#define ANSWER_SUFFIX ".ans"
// 如果使用 STDIN/STDOUT 则启用
#define STDIO
// =================================================

void exit_fail(int ret) {
    if (ret) exit(ret);
}

/**
 * 运行程序并测量时间与内存占用
 * - input_path: 输入文件(用于 STDIO 模式)
 * - output_path: 输出文件(用于 STDIO 模式)
 * - bin_args: 可执行文件相对 WORK_DIR 的运行参数(用于文件 I/O 模式)
 *
 * 返回 (wall_time_ms, max_memory_kb)
 */
std::pair<long long, long long> run_and_measure(
    const std::string& input_path,
    const std::string& output_path,
    const std::vector<std::string>& bin_args
) {
    using namespace std;

    auto start = chrono::steady_clock::now();

    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork");
        exit(1);
    }

    if (pid == 0)
    {
        // 子进程
        if (chdir(WORK_DIR) != 0)
        {
            perror("chdir");
            exit(1);
        }

#ifdef STDIO
        // STDIN/STDOUT 重定向
        if (!input_path.empty())
        {
            freopen(input_path.c_str(), "r", stdin);
        }
        if (!output_path.empty())
        {
            freopen(output_path.c_str(), "w", stdout);
        }

        // 构造 exec 参数
        std::vector<char*> args;
        args.push_back(const_cast<char*>(RUN_BINARY));
        args.push_back(nullptr);

        execvp(args[0], args.data());

#else
        // 文件 I/O 模式:直接传递参数,不做重定向
        // 构造 exec 参数
        std::vector<char*> args;
        args.push_back(const_cast<char*>(RUN_BINARY));
        for (auto &s : bin_args)
            args.push_back(const_cast<char*>(s.c_str()));
        args.push_back(nullptr);

        execvp(args[0], args.data());
#endif

        perror("execvp");
        exit(1);
    }

    // 父进程:等待子进程并收集 rusage
    int status;
    struct rusage usage{};
    wait4(pid, &status, 0, &usage);

    auto end = chrono::steady_clock::now();
    long long ms = chrono::duration_cast<chrono::milliseconds>(end - start).count();
    long long mem = usage.ru_maxrss;

    return {ms, mem};
}

int main(int argc, char** argv) {
    using namespace std;

    assert(argc == 3);
    int cases = stoi(argv[1]);
    string probtitle = argv[2];

    printf("%s [%d cases]\n", probtitle.c_str(), cases);

    for (int i = 1; i <= cases; i++)
    {
        printf("\nRunning test %d\n", i);

        string prefix = probtitle + "/" SAMPLE_PREFIX + probtitle + to_string(i);
        string in_path = prefix + INPUT_SUFFIX;
        string ans_path = prefix + ANSWER_SUFFIX;

        string local_in = WORK_DIR "/" + probtitle + INPUT_SUFFIX;
        string local_out = WORK_DIR "/" + probtitle + OUTPUT_SUFFIX;

        // 复制输入文件到工作目录/题目.in
        system(("cp " + in_path + " " + local_in).c_str());

#ifdef STDIO

        // 测试运行(STDIO 模式)
        auto [time_ms, mem] = run_and_measure(
            probtitle + INPUT_SUFFIX,
            probtitle + OUTPUT_SUFFIX,
            {}
        );

#else
        std::vector<std::string> args{};

        auto [time_ms, mem] = run_and_measure(
            "", "",  // 不用 STDIO
            args
        );
#endif

        printf("%.3fs, %.3fMB\n", time_ms / 1e3, mem / 1048576.0);
        // Checker
        exit_fail(system(
            (CHECKER " " + local_in + " " + local_out + " " + ans_path).c_str()
        ));
        system(("rm " + local_in).c_str());
        system(("rm " + local_out).c_str());
    }

    return 0;
}

评论

Dtw
niu

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。