有没有一种方法可以在C语言中读取文件而无需检查sscanf和fgets?

时间:2020-05-30 02:28:43

标签: c string input scanf

在我正在编写的程序中,我目前正在解析输入文件。我必须进行输入验证(在某种程度上),检查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 个答案:

答案 0 :(得分:1)

由于您遵循的是模式:

    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;

您可以创建自己的函数,将fgetssscanf函数的调用和检查合并为一个,这样代码就不会那么冗长。

类似的东西:

#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接收sscanf函数中使用的可变参数的数量。

答案 1 :(得分:0)

有没有一种方法可以在C语言中读取文件而无需检查sscanf和fgets?

是的。只需使用fgetc逐个读取字符,然后应用常用的lexingparsing技术。

阅读Dragon book

考虑使用parser generators,例如ANTLRflex + bisonlemon等,然后会从更高版本的解析语言的级别描述。

否则,如果您输入的解析语言不够怪异,请使用您自己的元编程技术。编写(也许以OcamlGuilePython之类的其他编程语言编写)一些metaprogram来生成C代码(也许为recursive descent parser)从更高级别的描述中手动编写。然后针对这种情况调整您的build automation(例如,向您的Makefile添加几行)。

请注意,解析是一种完善的技术。您将找到自1960年代以来有关解析(以及编译器构建和解释器构建)的研究论文。 您会发现大量的open source程序(例如在githubgitlab或其他地方)其解析应该对您有启发性:例如大多数C compilers(例如tinycc),Unix shells(例如bashzshsash,{{3} }等...

答案 2 :(得分:0)

从文件中获取数据的一种好方法是fscanf()。无需使用fgets或sscanf。您可以在下一行使用05/30/20,12:31,75.158,14.29,19.54,Biglobe,111.111.111.111

fscanf用作sscanf,但用于文件。其他规则都一样。

请检查我重写的代码。

import os
import re
import subprocess
import time

response = subprocess.Popen(‘/usr/local/bin/speedtest-cli’, shell=True, stdout=subprocess.PIPE).stdout.read().decode(‘utf-8’)

ping = re.findall(‘km]:\s(.*?)\s’, response, re.MULTILINE)
download = re.findall(‘Download:\s(.*?)\s’, response, re.MULTILINE)
upload = re.findall(‘Upload:\s(.*?)\s’, response, re.MULTILINE)
myip = re.findall(‘from\s(.*?)\s’, response, re.MULTILINE)

ping = ping[0].replace(‘,’, ‘.’)
download = download[0].replace(‘,’, ‘.’)
upload = upload[0].replace(‘,’, ‘.’)
myip = myip[0]

try:
f = open(‘/home/pi/speedtest/speedtestz.csv’, ‘a+’)
if os.stat(‘/home/pi/speedtest/speedtestz.csv’).st_size == 0:
f.write(‘Date,Time,Ping,Download (Mbit/s),Upload (Mbit/s),myip\r\n’)
except:
pass

f.write(‘{},{},{},{},{},{}\r\n’.format(time.strftime(‘%m/%d/%y’), time.strftime(‘%H:%M’), ping, download, upload, myip))
while(fgetc(fp) != '\n');
#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);

}