Есть ли способ прочитать файл в C без кучи проверок на sscanf и fgets? - PullRequest
0 голосов
/ 30 мая 2020

В программе, которую я пишу, я сейчас выполняю часть анализа входного файла. Мне нужно выполнить проверку ввода (до некоторой степени), проверяя, анализирует ли sscanf правильное количество переменных, а fgets не равно нулю. Но в результате основной контур выглядит так:

int LINE_LENGTH = 100;
int parseInput(FILE* fp, FILE* output) {
    char* line = calloc(LINE_LENGTH, sizeof(char));
    if (fgets(line, LINE_LENGTH, fp) == NULL) return 1;
    int camFlag, lightFlag;
    if (sscanf(line, "%d %d %d\n", &frames, &camFlag, &lightFlag) != 3) return 1;
    if (camFlag) {
        if (fgets(line, LINE_LENGTH, fp) == NULL) return 1;
        double cx, cy, cz, dx, dy, dz, dt;
        if (sscanf(line, "%f %f %f %f %f %f %f\n", &cx, &cy, &cz, &dx, &dy, &dz, &dt) != 7) return 1;
        // do stuff with input
    }
    if (lightFlag) {
        if (fgets(line, LINE_LENGTH, fp) == NULL) return 1;
        double cx, cy, cz;
        unsigned char r, g, b;
        if (sscanf(line, "%f %f %f %hhu %hhu %hhu\n", &cx, &cy, &cz, &r, &g, &b) != 6) return 1;
        // do stuff with this data
    }
    for (int i = 0; i < frames; i++) {
        if (fgets(line, LINE_LENGTH, fp) == NULL)) return 1;
        int n;
        if (sscanf(line, "%d\n", &n) != 1) return 1;
        // etc...
    }
}

и в результате половина строк проверяет ввод. Есть ли хороший способ избежать этого?

Ответы [ 3 ]

1 голос
/ 30 мая 2020

Поскольку вы следуете шаблону:

    if (fgets(line, LINE_LENGTH, fp) == NULL) return 1;
    int camFlag, lightFlag;
    if (sscanf(line, "%d %d %d\n", &frames, &camFlag, &lightFlag) != 3) return 1;

Вы можете создать свою собственную функцию для объединения вызовов и проверок функций fgets и sscanf в одну, таким образом код будет менее подробный.

Что-то вроде:

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

int LINE_LENGTH = 100;
int checked_fgets_sscanf(FILE *fp, int count, const char *fmt, ...)
{
    //char* line = calloc(LINE_LENGTH, sizeof(char));
    char line[LINE_LENGTH];
    if (fgets(line, LINE_LENGTH, fp) == NULL) return 1;
    va_list ap;
    va_start(ap, fmt);
    int rc = vsscanf(line, fmt, ap);
    va_end(ap);
    //free(line);
    return rc != count;
}

int main()
{

    int a, b;
    if(checked_fgets_sscanf(stdin, 2, "%d %d", &a, &b)) return 1;

    return 0;
}

Где параметр count функции принимает количество аргументов c переменных, используемых в функции sscanf.

0 голосов
/ 30 мая 2020

Один из хороших способов получить данные из файла - это fscanf (). Нет необходимости использовать fgets или sscanf. Вы можете использовать while(fgetc(fp) != '\n'); для следующей строки.

fscanf используется как sscanf, но для файла. другие правила такие же.

пожалуйста, проверьте код, который я его переписываю.

#include <stdio.h>
#include <stdlib.h>

int LINE_LENGTH = 100;
int parseInput(FILE* fp, FILE* output) {
    char* line = calloc(LINE_LENGTH, sizeof(char));
    int camFlag, lightFlag, frames;
    if (fscanf(fp, "%d %d %d", &frames, &camFlag, &lightFlag) != 3) return 1;
    printf("frames - %d, camFlag - %d, lightFlag - %d\n", frames, camFlag, lightFlag);
    while(fgetc(fp) != '\n');
    if (camFlag) {
        double cx, cy, cz, dx, dy, dz, dt;
        if (fscanf(fp, "%lf %lf %lf %lf %lf %lf %lf", &cx, &cy, &cz, &dx, &dy, &dz, &dt) != 7) return 1;
        printf("cx - %lf, cy - %lf, cz - %lf, dx - %lf, dy - %lf, dz - %lf, dt - %lf\n", cx, cy, cz, dx, dy, dz, dt);
        while(fgetc(fp) != '\n');
        // do stuff with input
    }
    if (lightFlag) {
        double cx, cy, cz;
        unsigned char r, g, b;
        if (fscanf(fp, "%lf %lf %lf %hhu %hhu %hhu", &cx, &cy, &cz, &r, &g, &b) != 6) return 1;
        printf("cx - %lf, cy - %lf, cz - %lf, r - %hhu, g - %hhu, b - %hhu\n",cx, cy, cz, r, g, b);
        while(fgetc(fp) != '\n');
        // do stuff with this data
    }
    for (int i = 0; i < frames; i++) {
        int n;
        if (fscanf(fp, "%d", &n) != 1) return 1;
        printf("n - %d\n", n);
        while(fgetc(fp) != '\n');
        // etc...
    }
}

int main(int argc, char ** argv)
{
        FILE * fp1 = NULL , * fp2 = NULL;
        fp1 = fopen(argv[1], "r");
        fp2 = fopen(argv[2], "w");
        parseInput(fp1, fp2);

}
file data: 
1 2 3
1.1 2.2 3.3 4.4 5.5 6.6 7.7
1.1 2.2 3.3 4 5 6
1
OUTUPT:

frames - 1, camFlag - 2, lightFlag - 3
cx - 1.100000, cy - 2.200000, cz - 3.300000, dx - 4.400000, dy - 5.500000, dz - 6.600000, dt - 7.700000
cx - 1.100000, cy - 2.200000, cz - 3.300000, r - 4, g - 5, b - 6
n - 1
0 голосов
/ 30 мая 2020

Есть ли способ прочитать файл в C без кучи проверок для sscanf и fgets?

Да. Просто прочтите символы один за другим с помощью fgetc, затем примените обычные методы лексирования и синтаксического анализа .

Прочтите книгу Дракона .

Рассмотрите возможность использования генераторов парсеров , таких как ANTLR , flex + bison , лимон и т. Д. 1073 * ... Затем будет сгенерирован код C из высокоуровневого описания анализируемого языка.

Или же, если ваш входной анализируемый язык достаточно странный, используйте свои собственные методы метапрограммирования. Напишите (возможно, на каком-нибудь другом языке программирования, например Ocaml или Guile или Python) какую-нибудь метапрограмму для генерации C код (возможно, какой-то парсер рекурсивного спуска ), который вы пишете вручную из описания более высокого уровня. Затем адаптируйте свою автоматизацию сборки (например, добавьте несколько строк в Makefile) для таких случаев.

Обратите внимание, что синтаксический анализ - хорошо зарекомендовавший себя метод . Вы найдете исследовательские работы по синтаксическому анализу (и построению компилятора и построению интерпретатора) с 1960-х годов. Вы найдете множество программ с открытым исходным кодом (например, на github или gitlab или где-либо еще) , анализ которых должен вдохновить вам: например, большинство C компиляторов (например, tiny cc), Unix shells например bash или zsh или sa sh, исходный код Python, et c ...

...