Hihi,大家好,這里是第一次發博客的computer!
今天打算把自己寫的一個簡單的測評程式發出來
(雖然通俗一點就是對拍的復雜版)
但不管怎么樣,它都比普通對拍高級多了!(不知道哪來的自信)
Anyway,那我們開始!
Introduction
首先我們要搞清楚我們要實作的功能:
1.實作代碼編譯及編譯是否成功(system函式就可以了)
2.生成亂數作為樣例輸入(這個需要根據題目輸入專門寫生成器)
(說白了就是懶)
3.對比自己寫的程式與標準(即AC程式)的輸出(用字串處理)
4.在report.out里面輸出評測結果:
1)如果是AC,則輸出AC程式和自己程式的輸出;
2)如果是WA,則輸出“Read <自己程式輸出>, expect <AC程式輸出>,
Tips
為了方便撰寫以及規范使用和方便講解,先把一些注意事項寫在這里:
1.將你的代碼和AC(即標準代碼)放到除錯器所在檔案夾中,將你的代碼命名為
ans.cpp,將標準代碼命名為std.cpp;
2.將樣例輸入存在sample.in中,并將sample.in放在上述檔案夾中,作為樣例輸入;
3.使用前請在ans.cpp中加入以下代碼:
freopen(“sample.in”,“r”,stdin);
freopen(“ans.out”,“w”,stdout);
請在std.cpp中加入以下代碼:
freopen(“sample.in”,“r”,stdin);
freopen(“std.out”,“w”,stdout);
以便除錯;
4.除錯器使用MinGW g++編譯器(32位64位都可行),請預先安裝
好,否則編譯失敗;
5.程式基于Windows系統撰寫,Linux和IOS暫時無(當然如果有大佬想要根據
我這個想法寫出Linux和IOS的版本我也非常歡迎-w-)/)
Programing
*編程之前的準備
*1.頭檔案
首先必不可少的兩位:
#include <iostream>
#include <cstdio>
由于需要用到system函式和處理字串,所以需要:
#include <cstring>
#include <windows.h>
同時,為了處理system函式的回傳值,我們需要定義兩個宏:
(因為我的函式庫里面的宏貌似壞掉了)
#define WEXITSTATUS(status) (((status) & 0xff00) >> 8)
#define WIFEXITED(status) ((status & 0x7f) == 0)
最后不要忘記這一句:
using namespace std;
*2.全域變數
定義最大存盤空間maxn和儲存自己答案和標準的字串:
(我用C的語法寫字串,因為C++的有些陰間)
const int maxn = 100000 + 5;
char ANS[maxn],STD[maxn] = "";
1.洗掉之前運行的結果
首先我們需要洗掉之前運行的結果(.out)和程式(.exe),防止它們對評測的影響;
system("del ans.out");
system("del std.out");
system("del report.out");
system("del ans.exe");
system("del std.exe");
如果運行時輸出找不到檔案是正常的,不用管,
2.判斷是否能打開sample.in
沒啥好說的,就是一個fopen回傳值的檢測
FILE *fp;
if ((fp = fopen("sample.in", "r") )== NULL)
{
printf("Usage: Can not open sample.in\n");
return 0;
}
fclose(fp);
3.編譯
接下來是我們要實作的第一個功能:編譯,
首先我們需要下載MinGW,并根據自己電腦進行配置,這邊我不過多贅述,
網上也有很多關于使用命令列編譯C++的例子,這里我貼一個鏈接:
https://blog.csdn.net/weixin_45676049/article/details/108018850
接著,為了適應32位和64位系統,我們需要寫一個判斷,為此,我們需要
利用變數型別的一個特性:
變數在不同位系統中的存盤位數是不同的,例如:
在32位系統中,sizeof(char*) == 4;
而在64位系統中,sizeof(char*) == 8;
然后為了偵測CE(即編譯錯誤),我們還要獲得system的回傳值,
system函式的回傳值很奇怪,一時半會說不清,我就直接給出結論了:
判斷一個system函式呼叫shell腳本是否正常結束的方法應該是如下3個條件
同時成立:
(1)-1 != status
(2)WIFEXITED(status)為真
(3)0 == WEXITSTATUS(status)
好了,說了這么多,直接來看代碼吧:
if(sizeof(char*) == 4)
{
int status_std32 = 0, status_ans32 = 0;
status_std32 = system("g++ -o std.exe -m32 std.cpp");
status_ans32 = system("g++ -o ans.exe -m32 ans.cpp");
if(status_ans32 == -1 || status_std32 == -1 || WIFEXITED(status_ans32) == false || WIFEXITED(status_std32) == false || 0 != WEXITSTATUS(status_ans32) || 0 != WEXITSTATUS(status_std32))
{
printf("CE:Compile Error");
return 0;
}
}
if(sizeof(char*) == 8)
{
int status_std = 0, status_ans = 0;
status_std = system("g++ -o std.exe std.cpp");
status_ans = system("g++ -o ans.exe ans.cpp");
if(status_ans == -1 || status_std == -1 || WIFEXITED(status_ans) == false || WIFEXITED(status_std) == false || 0 != WEXITSTATUS(status_ans) || 0 != WEXITSTATUS(status_std))
{
printf("CE:Compile Error");
return 0;
}
}
4.判斷程式是否運行成功
這一步主要是檢測是否是RE(即Runtime Error)
int outcheck_std,outcheck_ans;
outcheck_std = system("std.exe");
outcheck_ans = system("ans.exe");
if(outcheck_std == -1 || outcheck_ans == -1 || WIFEXITED(outcheck_std) == false || WIFEXITED(outcheck_ans) == false || 0 != WEXITSTATUS(outcheck_std) || 0 != WEXITSTATUS(outcheck_ans))
{
printf("RE:Runtime Error");
return 0;
}
5.將輸出檔案中的空格和回車去掉
這是因為很多讀入函式(例如scanf,gets)只能讀到空格或者回車就停止
了,所以需要單獨處理,這里將這個功能編程一個函式,
(主要是不知道為什么放在主函式中第一個處理了第二個沒處理啊啊啊)
void enter_deal(const char* filename)
{
int i,j = 0;
char s[10000];
FILE *p;
p=fopen(filename,"r");
fscanf(p,"%[^#]",s);
fclose(p);
for(int i=0;i<strlen(s);i++)
{
if(s[i]==' ' || s[i]=='\n')
continue;
s[j++]=s[i];
}
s[j]=0;
p=fopen(filename,"w");
fprintf(p,"%s",s);
fclose(p);
return;
}
然后呼叫即可:
enter_deal("ans.out");
enter_deal("std.out");
6.讀入ans和std的輸出檔案比較后輸出在report.out中
int count_ans = 0,count_std = 0;
freopen("std.out","r",stdin);
scanf("%s",STD);
freopen("ans.out","r",stdin);
scanf("%s",ANS);
int len_a = strlen(ANS);
int len_s = strlen(STD);
printf("ans:%s\n",ANS);
printf("std:%s\n\n",STD);
if(strcmp(ANS,STD) == 0)
printf("AC:Accepted\n");
else
printf("WA:Wrong Answer:\nRead %s,expect %s\n",ANS,STD);
return 0;
至此,我們的除錯測評程式就做完啦!
綜上所述,我們整理一下代碼:
//Compiler & Program evaluator Always work in progress
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <windows.h>
#define WEXITSTATUS(status) (((status) & 0xff00) >> 8)
#define WIFEXITED(status) ((status & 0x7f) == 0)
using namespace std;
const int maxn = 100000 + 5;
char ANS[maxn],STD[maxn] = "";
void enter_deal(const char* filename)
{
int i,j = 0;
char s[10000];
FILE *p;
p=fopen(filename,"r");
fscanf(p,"%[^#]",s);
fclose(p);
for(int i=0;i<strlen(s);i++)
{
if(s[i]==' ' || s[i]=='\n')
continue;
s[j++]=s[i];
}
s[j]=0;
p=fopen(filename,"w");
fprintf(p,"%s",s);
fclose(p);
return;
}
int main()
{
int i = 0,j = 0;
freopen("report.out","w",stdout);
system("del ans.out");
system("del std.out");
system("del report.out");
system("del ans.exe");
system("del std.exe");
FILE *fp;
if ((fp = fopen("sample.in", "r") )== NULL)
{
printf("Usage: Can not open sample.in\n");
return 0;
}
fclose(fp);
if(sizeof(char*) == 4)
{
int status_std32 = 0, status_ans32 = 0;
status_std32 = system("g++ -o std.exe -m32 std.cpp");
status_ans32 = system("g++ -o ans.exe -m32 ans.cpp");
if(status_ans32 == -1 || status_std32 == -1 || WIFEXITED(status_ans32) == false || WIFEXITED(status_std32) == false || 0 != WEXITSTATUS(status_ans32) || 0 != WEXITSTATUS(status_std32))
{
printf("CE:Compile Error");
return 0;
}
}
if(sizeof(char*) == 8)
{
int status_std = 0, status_ans = 0;
status_std = system("g++ -o std.exe std.cpp");
status_ans = system("g++ -o ans.exe ans.cpp");
if(status_ans == -1 || status_std == -1 || WIFEXITED(status_ans) == false || WIFEXITED(status_std) == false || 0 != WEXITSTATUS(status_ans) || 0 != WEXITSTATUS(status_std))
{
printf("CE:Compile Error");
return 0;
}
}
int outcheck_std,outcheck_ans;
outcheck_std = system("std.exe");
outcheck_ans = system("ans.exe");
if(outcheck_std == -1 || outcheck_ans == -1 || WIFEXITED(outcheck_std) == false || WIFEXITED(outcheck_ans) == false || 0 != WEXITSTATUS(outcheck_std) || 0 != WEXITSTATUS(outcheck_ans))
{
printf("RE:Runtime Error");
return 0;
}
enter_deal("ans.out");
enter_deal("std.out");
int count_ans = 0,count_std = 0;
freopen("std.out","r",stdin);
scanf("%s",STD);
freopen("ans.out","r",stdin);
scanf("%s",ANS);
int len_a = strlen(ANS);
int len_s = strlen(STD);
printf("ans:%s\n",ANS);
printf("std:%s\n\n",STD);
if(strcmp(ANS,STD) == 0)
printf("AC:Accepted\n");
else
printf("WA:Wrong Answer:\nRead %s,expect %s\n",ANS,STD);
return 0;
}
Test
首先我們要有道題:
P1045 [NOIP2003 普及組] 麥森數
題目描述
形如2^P -1的素數稱為麥森數,這時P一定也是個素數,但反過來不一定,即如果P是個素數,2^P-1不一定也是素數,到1998年底,人們已找到了37個麥森數,最大的一個是P=3021377P=3021377,它有909526位,麥森數有許多重要應用,它與完全數密切相關,
任務:從檔案中輸入P(1000<P<3100000),計算2^P - 1的位數和最后500位數字(用十進制高精度數表示)
輸入格式
檔案中只包含一個整數P(1000<P<3100000)
輸出格式
第一行:十進制高精度數2^P-1的位數,
第2-11行:十進制高精度數2^ P-1的最后500位數字,(每行輸出50位,共輸出10行,不足500位時高位補0)
輸入輸出樣例
輸入
1279
輸出
386
00000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000
00000000000000104079321946643990819252403273640855
38615262247266704805319112350403608059673360298012
23944173232418484242161395428100779138356624832346
49081399066056773207629241295093892203457731833496
61583550472959420547689811211693677147548478866962
50138443826029173234888531116082853841658502825560
46662248318909188018470682222031405210266984354887
32958028878050869736186900714720710555703168729087
這是ans.cpp:
//P1045 [NOIP2003 普及組] 麥森數
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
int P;
const unsigned long long int Base = 10000000000ULL;
const int maxn = 50 + 10;
unsigned long long val[maxn];
int main()
{
freopen("sample.in","r",stdin);
freopen("ans.out","w",stdout);
scanf("%d",&P);
printf("%d\n",(int)(P * log(2) / log(10)) + 1);
val[0] = 1;
while(P--)
{
for(int i = 0; i < 50; i++)
val[i] *= 2;
for(int i = 0; i < 50; i++)
{
if(val[i] >= Base)
{
val[i + 1] += val[i] / Base;
val[i] %= Base;
}
}
}
val[0]--;
for(int i = 49; i >= 0; i--)
{
printf("%010llu",val[i]);
if(i % 5 == 0)
printf("\n");
}
return 0;
}
這是std.cpp(從題解里面找的):
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int f[1001],p,res[1001],sav[1001];//乘法要開兩倍長度
void result_1()
{
memset(sav,0,sizeof(sav));
for(register int i=1;i<=500;i+=1)
for(register int j=1;j<=500;j+=1)
sav[i+j-1]+=res[i]*f[j];//先計算每一位上的值(不進位)
for(register int i=1;i<=500;i+=1)
{
sav[i+1]+=sav[i]/10;//單獨處理進位問題,不容易出錯
sav[i]%=10;
}
memcpy(res,sav,sizeof(res));//cstring庫里的賦值函式,把sav的值賦給res
}
void result_2()//只是在result_1的基礎上進行了細微的修改
{
memset(sav,0,sizeof(sav));
for(register int i=1;i<=500;i+=1)
for(register int j=1;j<=500;j+=1)
sav[i+j-1]+=f[i]*f[j];
for(register int i=1;i<=500;i+=1)
{
sav[i+1]+=sav[i]/10;
sav[i]%=10;
}
memcpy(f,sav,sizeof(f));
}
int main()
{
freopen("sample.in","r",stdin);
freopen("std.out","w",stdout);
scanf("%d",&p);
printf("%d\n",(int)(log10(2)*p+1));
res[1]=1;
f[1]=2;//高精度賦初值
while(p!=0)//快速冪模板
{
if(p%2==1)result_1();
p/=2;
result_2();
}
res[1]-=1;
for(register int i=500;i>=1;i-=1)//注意輸出格式,50個換一行,第一個不用
if(i!=500&&i%50==0)printf("\n%d",res[i]);
else printf("%d",res[i]);
return 0;
}
為了方便測驗,我都在代碼里面加了檔案輸入輸出,
因為資料缺少,我們就只能用樣例進行測驗
sample.in內容:1279
運行后:
ans.out:
38600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010407932194664399081925240327364085538615262247266704805319112350403608059673360298012239441732324184842421613954281007791383566248323464908139906605677320762924129509389220345773183349661583550472959420547689811211693677147548478866962501384438260291732348885311160828538416585028255604666224831890918801847068222203140521026698435488732958028878050869736186900714720710555703168729087
std.out:
38600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010407932194664399081925240327364085538615262247266704805319112350403608059673360298012239441732324184842421613954281007791383566248323464908139906605677320762924129509389220345773183349661583550472959420547689811211693677147548478866962501384438260291732348885311160828538416585028255604666224831890918801847068222203140521026698435488732958028878050869736186900714720710555703168729087
report.out:
D:\Program\ProgramEvaluator\report.out
ans:38600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010407932194664399081925240327364085538615262247266704805319112350403608059673360298012239441732324184842421613954281007791383566248323464908139906605677320762924129509389220345773183349661583550472959420547689811211693677147548478866962501384438260291732348885311160828538416585028255604666224831890918801847068222203140521026698435488732958028878050869736186900714720710555703168729087
std:38600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010407932194664399081925240327364085538615262247266704805319112350403608059673360298012239441732324184842421613954281007791383566248323464908139906605677320762924129509389220345773183349661583550472959420547689811211693677147548478866962501384438260291732348885311160828538416585028255604666224831890918801847068222203140521026698435488732958028878050869736186900714720710555703168729087
AC:Accepted
證明我們的程式成功了!
Rethink
做完這個簡易測評程式后,我發現還是有很多不足,比如沒有檢測TLE(跑超時間)等等,但不管怎么
說,我重新學習了一遍system函式的回傳值,更加熟練地運用了字串對比函式strcmp和freopen的檔案
輸入輸出,這個突然興起的小專案,也鍛煉了我的編程能力,同時讓我了解到編程,還是有很長的路,
要走,
最后,感謝你閱讀這篇博客!
如果有錯,請在評論區中指出!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/295147.html
標籤:其他
上一篇:基于SSM的培訓機構管理系統
下一篇:IDEA快速生成構造器(建構式)
