在我正在编写的程序中,我目前正在解析输入文件。我必须进行输入验证(在某种程度上),检查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...
}
}
因此,一半的行正在检查输入。有避免这种情况的好方法吗?
由于您遵循的是模式:
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
接收sscanf
函数中使用的可变参数的数量。
是否有一种无需使用sscanf和fgets进行大量检查就可以读取C中文件的方法?
是。只需使用fgetc
逐个读取字符,然后应用常规的lexing和parsing技术。
读取Dragon book。
考虑使用parser generators,例如ANTLR,flex + bison,lemon等...,然后将从更高层次的解析语言描述中生成一些C代码。
否则,如果您输入的解析语言足够怪异,请使用您自己的元编程技术。编写(也许以Ocaml或Guile或Python之类的其他编程语言编写)某些metaprogram来生成您要从更高级别的描述中手动编写的C代码(也许是recursive descent parser)。然后针对这种情况调整您的build automation(例如,在Makefile
中添加几行)。
注意解析是一种完善的技术。您会发现自1960年代以来有关解析(以及编译器构建和解释器构建)的研究论文。 您会发现大量的Makefile
程序](例如,在open source或github或其他位置)其解析对您而言应该是鼓舞人心的:大多数C gitlab(例如compilers),Unix tinycc(例如shells或bash或zsh),sash的源代码,等等...
从文件获取数据的一种好方法是fscanf()。无需使用fgets或sscanf。您可以在下一行使用Python。
fscanf用作sscanf,但用于文件。其他规则相同。
请检查我重写的代码。
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);
}
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