gpt4 book ai didi

c - C 中的格式检查

转载 作者:行者123 更新时间:2023-12-02 20:00:20 25 4
gpt4 key购买 nike

我正在尝试用 c 编写一个程序,从文本中读入溜冰场事件的大型数据库。有谁知道如何检查不符合格式的内容

即文本文档将具有类似这样的内容

   sample
---------------------------------------------
date startT endT END
_______________________________________________



Ice Rink 1

1/13/2014 1:50 3:50 PM Public Skating

1/13/2014 1:50 3:50 PM Game

ice rink 2

1/13/2014 1:50 3:50 PM OPEN

我已经可以成功读取一行事件、日期时间和描述但如何跳过或检测与我的扫描风格不匹配的行

fscanf(ifp,"%d/%d/%d\t%d:%d%s\t%d:%d%s\t\t  %20c",
&e1[i].month,&e1[i].day,&e1[i].year,&e1[i].startH,&e1[i].startM,e1[i].MER1,&e1[i].endH,&e1[i].endM,e1[i].MER2,e1[i].event);

简而言之:如何检测与此不完全匹配的案例?

提前致谢

最佳答案

正如其他人已经说过的,您可以检查 fscanf 的返回值来确定一行是否符合给定的格式。然而,这并不是理想的方法。首先,您的数据是按行组织的,但 fscanf 将换行符视为任何其他空格。您可以先使用 fgets 读取该行,然后在该行上应用 sscanf,但您仍然会遇到一个很容易丢失的大型整体格式说明符。

我想提出另一种方法。您的数据行似乎是按字段组织的,这些字段之间用制表符分隔。您可以使用 fgets 读取这些行,然后使用 strtok 分割它们,最后使用 sscanf 扫描单独的字段。如果您将自定义包装函数写入 sscanf 语句,则可以在读取数据时对其运行健全性检查。

/*
* Return true if str has format "hh:min AM/PM"
*/
int scan_time(const char *str, int *hh, int *mm)
{
char buf[4] = {0};
int n;
char c;

n = sscanf(str, "%d:%d%4s %c", hh, mm, buf, &c);

if (n == 4) return 0; /* trailing extra chars */
if (n < 2) return 0; /* missing minutes */

if (n == 3) {
int key = (buf[0] << 16) + (buf[1] << 8) + buf[2];

#define KEY(a, b) ((a << 16) + (b << 8))

switch (key) {
case KEY('a', 'm'):
case KEY('A', 'M'):
break;

case KEY('p', 'm'):
case KEY('P', 'M'):
*hh += 12;
break;

default:
return 0; /* invalid am/pm spec */
}
}

if (*hh < 0 || *hh >= 24) return 0; /* invalid hours */
if (*mm < 0 || *mm >= 60) return 0; /* invalid minutes */

return 1;
}

/*
* Return true, if str has format "mm/dd/year"
*/
int scan_date(const char *str, int *yy, int *mm, int *dd)
{
static const int mdays[] = {
0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
int n;
char c;

n = sscanf(str, "%d/%d/%d %c", mm, dd, yy, &c);

if (n == 4) return 0; /* trailing extra chars */
if (n < 2) return 0; /* missing day */
if (n == 2) *yy = 2014; /* set default value */
if (*yy < 100) *yy += 2000; /* allow 1/1/14 */

if (*mm < 1 || *mm > 12) return 0; /* invalid month */
if (*dd < 1 || *dd > mdays[*mm]) return 0;
if (*mm == 2 && *dd == 29 % *yy % 4) return 0;
/* invalid day */
return 1;
}

/*
* Return true if line is "date \t time \t time \t text"
*/
int scan_line(char *str, struct Event *ev)
{
char *token;

token = strtok(str, "\t");
if (token == NULL) return 0;
if (!scan_date(token, &ev->year, &ev->month, &ev->day)) return 0;

token = strtok(NULL, "\t");
if (token == NULL) return 0;
if (!scan_time(token, &ev->startH, &ev->startM)) return 0;

token = strtok(NULL, "\t");
if (token == NULL) return 0;
if (!scan_time(token, &ev->endH, &ev->endM)) return 0;

token = strtok(NULL, "\t");
if (token == NULL) return 0;
strncpy(ev->event, token, 40);

return 1;
}

/*
* Remove trailing newline
*/
void chomp(char *str)
{
int l = strlen(str);

if (l && str[l - 1] == '\n') str[l - 1] = '\0';
}

/*
* Scan file with events
*/
int scan_file(const char *fn)
{
FILE *f = fopen(fn, "r");

if (f == NULL) return -1;
for (;;) {
struct Event ev;
char line[200];

if (fgets(line, 200, f) == NULL) break;
chomp(line);
if (scan_line(line, &ev)) {
printf("%s on %d/%d/%d\n",
ev.event, ev.month, ev.day, ev.year);
}
}
return 0;
}

这里,scan_xxx函数扫描一段数据,检查格式,分配数据并对数据运行基本检查,这样你就永远不会在32日收到事件一月或 35:00。

这使得扫描函数比单个调用 sscanf 更加复杂,但也有一些好处。首先,在读取格式时进行检查。这意味着您不必检查客户端代码中的数据,因为您可以依赖合理的值。这也意味着您不必重复代码:请注意,时间检查如何仅编码一次,即在 scan_time 中,尽管每行针对开始时间和结束时间应用两次.

在封装函数中按字段处理数据允许您更改格式。例如,您可以允许“1pm”作为“1:00 pm”的有效快捷方式。当第一种格式失败时,您只需使用第二种格式字符串重新扫描时间字段。您也可以使用长单行格式来做到这一点,但由于您有两个时间字段,所以这不会那么容易。

另请注意上面的代码如何接受 14 作为 2014 年的快捷方式,并将缺失的年份解释为 2014 年。对于一个简单的数据扫描工具来说,所有这些可能看起来有点太复杂,但您可以在类似的项目中重复使用您的函数。此外,编写这些整洁的函数比争论冗长的 scanf 格式更有趣。

关于c - C 中的格式检查,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21227873/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com