事情开始于大二上学期,有一门叫作《网站建设与管理》的课程。本人因为对Web方面比较有兴趣,又比较喜欢Python语言的风格,因而就边学老师教的PHP边学了Django。正好大一下学期学了算法,对算法和各类OJ都比较有兴趣,因此就写了一个不能评测的手动OJ当期末大做业交了上去。这个暑假开始着手将这个网站建成一个能评测的真OJ。在知乎和Github上爬了一段时间,找到了https://github.com/KIDx/Judger这么一个开源项目,把这个项目的代码改动了很多以后终于能够跑起来了。本身的OJ地址在:http://111.230.131.247/JudgeHome/ios
目前使用以后发现原OJ系统对Presentation Error的断定过于严格,因而这里想将它改成去除行末空格与文末空行后比对的模式。将程序中与文本比对相关的算法提取出来,获得了一个用于文本比对的小程序:git
1 #include <iostream> 2 using namespace std; 3 4 void compare_until_nonspace(int &c_std, int &c_usr, FILE *&fd_std, FILE *&fd_usr, int &ret) 5 { 6 while((isspace(c_std)) || (isspace(c_usr))) 7 { 8 if (c_std != c_usr) 9 { 10 if (c_std == '\r' && c_usr == '\n') 11 { 12 c_std = fgetc(fd_std); 13 if (c_std != c_usr) 14 ret = 3; //PRESENTATION ERROR 15 } 16 else 17 { 18 ret = 3; //PRESENTATION ERROR 19 } 20 } 21 if (isspace(c_std)) 22 c_std = fgetc(fd_std); 23 if (isspace(c_usr)) 24 c_usr = fgetc(fd_usr); 25 } 26 } 27 28 int tt_compare_output(string &file_std, string &file_usr) 29 { 30 int ret = 1; //ACCEPTED 31 int c_std, c_usr; 32 FILE *fd_std = fopen(file_std.c_str(), "r"); 33 FILE *fd_usr = fopen(file_usr.c_str(), "r"); 34 35 if (fd_std == NULL) 36 { 37 printf("%s open standard file failed %s\n", strerror(errno), file_std.c_str()); 38 } 39 40 if (!fd_std || !fd_usr) 41 { 42 ret = -1; //RUNTIME ERROR (ABORTION) 43 } 44 else 45 { 46 c_std = fgetc(fd_std); 47 c_usr = fgetc(fd_usr); 48 for(;;) 49 { 50 compare_until_nonspace(c_std, c_usr, fd_std, fd_usr, ret); 51 while(!isspace(c_std) && !isspace(c_usr)) 52 { 53 // LOG_DEBUG("std: %c usr: %c", c_std, c_usr); 54 if (c_std == EOF && c_usr == EOF) 55 goto end; 56 if (c_std != c_usr) 57 { 58 ret = 2; //WRONG ANSWER 59 goto end; 60 } 61 c_std = fgetc(fd_std); 62 c_usr = fgetc(fd_usr); 63 } 64 } 65 } 66 end: 67 if (fd_std) 68 fclose(fd_std); 69 if (fd_usr) 70 fclose(fd_usr); 71 return ret; 72 } 73 74 int main() { 75 string file_std = "std.txt"; 76 string file_usr = "usr.txt"; 77 int res = tt_compare_output(*&file_std, *&file_usr); 78 switch(res) { 79 case -1: cout << "RUNTIME ERROR (ABORTION)" << endl; break; 80 case 1: cout << "ACCEPTED" << endl; break; 81 case 2: cout << "WRONG ANSWER" << endl; break; 82 case 3: cout << "PRESENTATION ERROR" << endl; break; 83 default: cout << "SYSTEM ERROR" << endl; 84 } 85 return 0; 86 }
测试该程序,可知usr.txt与std.txt只要有任何一点微小的差异,程序就会输出PRESENTATION ERROR。增长了以下一个逻辑简单的函数以后,就能够实现先将标准输出和选手输出中的行末空格和文末空行都去掉再进行比较了。github
void remove_blank(string file) { //删除文末空行和行末空格 fstream target_file(file.c_str(), fstream::in | fstream::out); string line; //做为读取的一行 string temp; //用做缓存 vector<string> obj; //用于储存整理完成后的每一行 if(!target_file) { cout << "Can't open target file!" << endl; //上线后改为LOG_DEBUG } while(getline(target_file, line)) { unsigned long len = line.length(); int count = 0; for(unsigned long i=len-1; i>=0; i--) { int temp_char = line[i]; //用于将字符转换为ASCII码,保存ASCII码使用 if(isspace(temp_char)) count++; else break; } temp = line.substr(0, len-count); obj.push_back(temp); } int count_lines = 0; //文末空行的数量 for(unsigned long i=obj.size()-1; i>=0; i--) { //去除文末空行 if(obj[i].empty()) count_lines++; else break; } cout << "count_lines: " << count_lines << endl; for(int i=0; i<count_lines; i++) obj.pop_back(); //使用ofstream打开再关闭文件是为了清空源文件 ofstream empty_file(file.c_str()); empty_file.close(); //从新打开文本文件 fstream target(file.c_str(), fstream::out | fstream::in); if(!target) { cerr << "Can't open file" << endl; //上线后改为LOG_DEBUG } //写回源文件 for(unsigned long i=0; i<obj.size(); i++) target << obj[i] << endl; target.close(); }
要注意的是,在Linux的g++编译器下,fstream、ofstream等的参数须要是c类型的字符串,须要将普通的string类型经过c_str转换为c类型的字符串才能够经过编译。而本人所使用的mac下clang编译器并无这种要求,直接使用string类型也能够经过编译。这里debug了好久,必定要当心。算法