我的教授希望我创建一个程序,自动为她的班级编译和评分学生作业。我知道已经有比这更充实的开源项目,但我决定编写这个代码是为了好玩。添加计时器以使学生程序不会永远继续后,.csv 文档的输出变得很奇怪。它重新打印了标题以及已经评分的学生 ID/年级
这是代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define MAX_COMMAND_LENGTH 1000
#define LOG_FILE_PATH "error_log.txt"
void logError(char* message, char* studentid, char* assignmentid, char* weekid) {
FILE* logFile = fopen(LOG_FILE_PATH, "a");
if (logFile) {
fprintf(logFile, "Error: %s %s %s %s\n", message, studentid, weekid, assignmentid);
fclose(logFile);
}
}
void timeoutHandler(int signum) {
//this function is called when the timeout occurs
//logError("Execution timed out.");
exit(1);
}
int grade(char* studentid, char* assignmentid, int questionCount, char* weekid) {
//define paths
char c_program_path[32];
char input_file_path[32];
char answer_file_path[32];
char compile_command[MAX_COMMAND_LENGTH];
sprintf(c_program_path, "%s/%s/%s_%s.c", weekid, studentid, studentid, assignmentid);
sprintf(compile_command, "gcc %s -o compiled_program.out", c_program_path);
//set up a timeout handler using the alarm function
signal(SIGALRM, timeoutHandler);
//Fork for grading
pid_t child_pid = fork();
if (child_pid == -1) {
perror("fork");
return 1;
}
if (child_pid == 0) { //this code runs in the child process
// set a timer for the timeout duration
//compile Program
//printf("\n1--1\n");
if (system(compile_command) != 0) {
printf("Compilation successful.\n");
exit(5);
}
alarm(2);
for (int i = 0; i < questionCount; i++){
//define input and answer paths
sprintf(input_file_path, "%s/input%d.txt\0", weekid, i);
sprintf(answer_file_path, "%s/answer%d.txt\0", weekid, i);
//open files
FILE* input_file = fopen(input_file_path, "r");
FILE* answer_file = fopen(answer_file_path, "r");
if (!input_file) {
exit(4);
}
if (!answer_file) {
exit(4);
}
//execute the compiled program using input redirection
//define variables for program execution
char compiled_program_path[MAX_COMMAND_LENGTH];
snprintf(compiled_program_path, sizeof(compiled_program_path), "%s", "./compiled_program.out");
char execution_command[MAX_COMMAND_LENGTH];
snprintf(execution_command, sizeof(execution_command), "%s < %s", compiled_program_path, input_file_path);
//execute program
FILE* program_output = popen(execution_command, "r");
if (!program_output) {
exit(3);
}
//compare output to answer
char bufferAnswer[128];
char bufferOutput[128];
while (fgets(bufferAnswer, sizeof(bufferAnswer), answer_file)) {
if (fgets(bufferOutput, sizeof(bufferOutput), program_output) == NULL) {
exit(2);
}
for (int j = 0; j < strlen(bufferAnswer)-2; j++){
if (bufferOutput[j] != bufferAnswer[j]){
exit(2);
}
}
}
//printf("\ntest\n");
// close files and cleanup
fclose(input_file);
fclose(answer_file);
pclose(program_output);
}
// clean up by removing the compiled program
remove("compiled_program.out");
printf("Execution successful.\n");
// cancel the timeout
alarm(0);
exit(0);
}else{
// wait for the child process to complete
int status;
waitpid(child_pid, &status, 0);
// check grade
int exit_status = WEXITSTATUS(status);
if (exit_status == 5){
logError("Compilation failed.", studentid, assignmentid, weekid);
}else if (exit_status == 0) {
// The child process exited successfully
printf("Child process completed successfully.\n");
return 0;
} else if (exit_status == 1) {
remove("compiled_program.out");
logError("Child process timed out.", studentid, assignmentid, weekid);
} else if (exit_status == 2) {
remove("compiled_program.out");
logError("Incorrect output.", studentid, assignmentid, weekid);
}else if (exit_status == 3){
remove("compiled_program.out");
logError("Error executing the compiled program.", studentid, assignmentid, weekid);
}else if (exit_status == 4){
remove("compiled_program.out");
logError("Error opening files.", studentid, assignmentid, weekid);
}else{
remove("compiled_program.out");
logError("Unknown error.", studentid, assignmentid, weekid);
}
return 1;
}
}
int main(){
//define students, week, assignment, question count, grading file
char gradesPath[32] = {"gradestest.csv"};
char* studentid[4] = {"std1\0", "std2\0", "std3\0", "std4\0"};
char* assignmentid[1] = {"test\0"};
char* weekid = "test\0";
int questionCount = 2;
//open grading file
FILE* gradesFile = fopen(gradesPath, "w");
if (!gradesFile) {
printf("error opening gradesFile");
return 1;
}
//write header
printf("writing header\n");
fprintf(gradesFile, "StudentID,");
for (int i = 0; i < 1; i++){
fprintf(gradesFile, "%s,", assignmentid[i]);
}
//fprintf(gradesFile, "\n");
printf("grading\n");
//grade each student
for (int i = 0; i < 4; i++){
printf("grading %s\n", studentid[i]);
fprintf(gradesFile, "\n%s,", studentid[i]);
for (int j = 0; j < 1; j++){
if (grade(studentid[i], assignmentid[j], questionCount, weekid)){
fprintf(gradesFile, "0,");
}else{
fprintf(gradesFile, "1,");
}
}
}
fprintf(gradesFile, "\n");
//close files and cleanup
fclose(gradesFile);
printf("fin\n");
return 0;
}
老实说,我不知道为什么会发生这种情况……孩子甚至不应该打印标题。我想知道 fprintf 是否有问题,但这似乎也不太可能
这是使用此程序打印的 CSV 文档。
StudentID,test,
std1,StudentID,test,
std1,1,
std2,StudentID,test,
std1,1,
std2,0,
std3,StudentID,test,
std1,1,
std2,0,
std3,0,
std4,StudentID,test,
std1,1,
std2,0,
std3,0,
std4,0,
对文件的输出进行缓冲,并在进程分叉时复制缓冲区。在调用
fflush
之前使用 fork
刷新流缓冲区,与 fflush(gradesFile);
一样。