БД - это сильно. И не очень нужно.
Вот изменённый код.
//v0.2
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
const int MAX_SIZE_TEXT = 10240; // 10 kB
const char *tags[] = {"<q>", "</q>", "<a>", "</a>", "<m>", "</m>"};
enum QuestionState
{
QS_INVALID,
QS_NOT_ANSWERED,
QS_WRONG,
QS_RIGHT,
};
struct Question
{
char *m_pQuestion;
char *m_pAnswer;
int m_mark;
QuestionState m_state;
};
// Копирует содержимое файла в данную строку C
// Принимает указатели на файл и строку
// Возвращает количество скопированных символов
int filetostring(FILE *_pFile, char *_pString)
{
size_t n;
int count = 0;
do
{
n = fread(_pString, sizeof(char), 4096, _pFile);
count += n;
}
while(n == 4096);
return count;
}
// Считает кол-во данных слов в строке Си
// Принимает указатели на строку и слово
// Возвращает кол-во слов
int countwords(const char *_pText, const char *_pWord)
{
int res = 0;
char *pBuf;
while(1)
{
pBuf = (char *) strstr(_pText, _pWord);
if(!pBuf)
break;
res++;
_pText = pBuf + strlen(_pWord);
}
return res;
}
// Возвращает текст между заданными тегами.
// Внимание! Создаёт динамический массив
char* findbetweentags(const char *_pText, const char *_pOpeningTag, const char *_pClosingTag)
{
char *pBuf1 = (char*) strstr(_pText, _pOpeningTag) + strlen(_pOpeningTag);
char *pBuf2 = (char*) strstr(_pText, _pClosingTag);
if(!pBuf1 || !pBuf2)
return NULL;
int len = pBuf2 - pBuf1;
if(len <= 0)
return NULL;
char *pStr = new char[len + 1];
strncpy(pStr, pBuf1, len);
return pStr;
}
// Выполняет конъюнкцию состояний ответа в массиве структур Question
// Принимает указатель на массив и кол-во его элементов
// Возвращает результат конъюнкции
bool conjunctQuestionArrayState(Question *_pQ, int nElems)
{
for(int i = 0; i < nElems; i++)
{
if(_pQ[i].m_state == QS_NOT_ANSWERED)
return 0;
}
return 1;
}
// Проверяет правильность ответа
// Принимает ссылку на структуру Question и указатель на данный ответ
// (строка Си)
// Возвращает кол-во баллов, полученных за ответ
int checkAnswer(Question &_rQuestion, const char *_pAnswer)
{
if(!strcmp(_pAnswer, _rQuestion.m_pAnswer))
{
_rQuestion.m_state = QS_RIGHT;
return _rQuestion.m_mark;
}
else
{
_rQuestion.m_state = QS_WRONG;
return 0;
}
}
enum LoopOp
{
LO_INVALID,
LO_PLUS,
LO_MINUS,
};
// Меняет значение числа "по кругу". Когда значение достигает максимума,
// оно становится минимальным (или наоборот)
// Принимает ссылку на переменную int, операцию, минимальное и
// максимальное значение
// Возвращает код ошибки
// 0: успешное завершение
// 1: неправильный минимум или максимум
// 2: неправильная операция
int loop(int &_rNum, LoopOp _op, int _min, int _max)
{
if(_min >= _max)
return 1;
switch(_op)
{
case LO_PLUS:
if(_rNum < _max)
_rNum++;
else
_rNum = _min;
break;
case LO_MINUS:
if(_rNum > _min)
_rNum--;
else
_rNum = _max;
break;
default:
return 2;
}
return 0;
}
// Выводит статусы вопросов из заданного массива Question
// Принимает указатель на массив Question, размер массива и режим вывода
// Порядок символов в режиме должен быть таким:
// не отвеченный, неправильный, правильный, другой/ошибка
// Если символов будет меньше 4, вывода не будет
void printStates(Question *_pQuestionArray, int _size, const char *_pShowMode = "NXVE")
{
if(strlen(_pShowMode) < 4)
return;
for(int i = 0; i < _size; i++)
switch(_pQuestionArray[i].m_state)
{
case QS_NOT_ANSWERED:
fputc(_pShowMode[0], stdout);
break;
case QS_WRONG:
fputc(_pShowMode[1], stdout);
break;
case QS_RIGHT:
fputc(_pShowMode[2], stdout);
break;
default:
fputc(_pShowMode[3], stdout);
}
fputc('\n', stdout);
}
int main(int argc, char** argv)
{
// Блок 1 - открытие файла
char path[255];
char text[MAX_SIZE_TEXT];
printf("Input path to test file: ");
fgets(path, 255, stdin);
if(path[strlen(path) - 1] == '\n')
path[strlen(path) - 1] = 0; // КОСТЫЛЬ!
else return 100;
FILE *fText = fopen(path, "rb");
if(!fText)
{
fprintf(stderr, "Failed to open file '%s'.\n", path);
return 1;
}
filetostring(fText, text);
fclose(fText);
// Блок 2 - разбор файла
int nQ = countwords(text, "<q>");
for(int i = 1; i < 6; i++)
{
int n = countwords(text, tags[i]);
if(n != nQ || n == 0)
{
fprintf(stderr, "File '%s' is corrupted.\n", path);
return 2;
}
}
Question *pQAM = new Question[nQ];
char *pTemp = text;
for(int i = 0; i < nQ; i++)
{
pQAM[i].m_pQuestion = findbetweentags(pTemp, tags[0], tags[1]);
pQAM[i].m_pAnswer = findbetweentags(pTemp, tags[2], tags[3]);
char *pMark = findbetweentags(pTemp, tags[4], tags[5]);
pQAM[i].m_mark = atoi(pMark);
delete [] pMark;
pQAM[i].m_state = QS_NOT_ANSWERED;
pTemp = strstr(pTemp, tags[5]) + strlen(tags[5]);
}
// Блок 3 - опрос
char answer[255];
int curQ = 0;
int markSum = 0;
int *marks = new int[nQ];
bool allAnswered = 0;
do
{
allAnswered = conjunctQuestionArrayState(pQAM, nQ);
if(allAnswered)
break;
system("clear");
printStates(pQAM, nQ);
printf("%s\n", pQAM[curQ].m_pQuestion);
fgets(answer, 255, stdin);
answer[strlen(answer) - 1] = 0; // КОСТЫЛЬ!
switch(answer[0])
{
case '<':
loop(curQ, LO_MINUS, 0, nQ - 1);
break;
case '>':
loop(curQ, LO_PLUS, 0, nQ - 1);
break;
case '?':
allAnswered = 1;
break;
default:
marks[curQ] = checkAnswer(pQAM[curQ], answer);
markSum += marks[curQ];
loop(curQ, LO_PLUS, 0, nQ - 1);
}
}
while(!allAnswered);
system("clear");
printStates(pQAM, nQ);
printf("Your mark: %d\n", markSum);
for(int i = 0; i < nQ; i++)
{
printf("%d:\t%3d of %3d\n", i + 1, marks[i], pQAM[i].m_mark);
}
// Удаление динамических массивов
for(int i = 0; i < nQ; i++)
{
delete [] pQAM[i].m_pQuestion;
delete [] pQAM[i].m_pAnswer;
}
delete [] pQAM;
delete [] marks;
return 0;
}