虽然您可以使用指针和(或通过简单地将指针沿缓冲区向下移动)来自由解析每一行的信息strchr
,但在处理格式化输入时,使用格式化输入函数可能会有sscanf
很大帮助。
让我们来看看两者。在您的问题中,您想用fgets
(good) 阅读,然后找到第一个逗号,然后将该行的其余部分复制到新缓冲区。到目前为止一切都很好。唯一需要注意的是,一旦找到第一个逗号,您仍然必须将指针从逗号前移,跳过任何空格,直到在剩余时间(该行的其余部分)到达第一个字符。
一种简单的方法,即在第一个逗号之后输出字符串的其余部分,省略任何中间空格,可以是:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXC 1024 /* if you need a constant, define one (or more) */
int main (void) {
char buf[MAXC];
while (fgets (buf, MAXC, stdin)) { /* read with fgets */
char *p;
buf[strcspn (buf, "\r\n")] = 0; /* trim '\n' from end */
if ((p = strchr (buf, ','))) { /* find 1st ',' */
do
p++; /* advance pointer */
while (*p && isspace (*p)); /* to end or 1st non-whitespace */
}
printf ("rest of string: '%s'\n", p); /* output rest of line */
}
}
(注意:声明常量时,不要吝啬缓冲区大小。)
示例输入文件
$ cat dat/airport_times.txt
KOCH, 01:01, 01:51
KAXX, 03:50, 05:40
KADS, 07:40, 09:30
示例使用/输出
$ ./bin/airport_gettimes <dat/airport_times.txt
rest of string: '01:01, 01:51'
rest of string: '03:50, 05:40'
rest of string: '07:40, 09:30'
现在让我们看看使用fgets
read 和 formatted-input 函数sscanf
来解析每一行中所有需要的值。(或者您可以fscanf
在一次调用中使用它,但您最好使用fgets
然后sscanf
(1)确保您使用整行数据并且(2)可以独立验证读取和解析值。
方法类似,用 读取fgets
,但不是用 查找第一个逗号,而是strchr
在sscanf
一次调用中将 ICAO、到达时间和离开时间读取到单独的缓冲区中。始终验证任何scanf
函数的返回,以确保发生预期的转换次数。
提前考虑,我们不要只使用三个独立且不相关的缓冲区。相反,让我们声明 astruct
来保存所有三个icao, ariv & dept
缓冲区。这样,如果将多个条目读入内存,您可以简单地声明一个结构数组并为以后的用户读取/解析所有值。(数组留给你)
这里的一个简单示例可能是:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXC 1024 /* if you need a constant, define one (or more) */
#define ICAO 6 /* will work for ICAO and time buffer lengths */
typedef struct {
char icao[ICAO],
ariv[ICAO],
dept[ICAO];
} flight_t;
int main (void) {
char buf[MAXC];
while (fgets (buf, MAXC, stdin)) { /* read with fgets */
flight_t flt = { .icao = "" }; /* declare struct */
/* separate all value in line into struct, validating return */
if (sscanf (buf, "%5[^,], %5[^,], %5[^,]",
flt.icao, flt.ariv, flt.dept) == 3)
/* if sscanf succeeds, output (or use) the values */
printf ("%s %s (arrival) %s (departure)\n",
flt.icao, flt.ariv, flt.dept);
}
}
(注意:如何在每个字符类转换说明符之前使用字段宽度修饰符来保护每个缓冲区的数组边界。如果您对格式字符串有疑问,请询问。)"%5[^,], %5[^,], %5[^,]"
相同的输入文件。
示例使用/输出
$ ./bin/airport_parsetimes <dat/airport_times.txt
KOCH 01:01 (arrival) 01:51 (departure)
KAXX 03:50 (arrival) 05:40 (departure)
KADS 07:40 (arrival) 09:30 (departure)
这里的好处是,您现在将所有值协调为单个对象,但独立存储,因此您可以根据需要访问任何icao, ariv
或dept
值,而在结构数组的情况下,仍然能够对任何数组进行排序结构成员,同时保持国际民航组织、到达和离开时间的关联。
将拼图的各个部分拼凑在一起的方法有很多很多。下一步是动态分配结构数组,并realloc
根据需要允许您读取未知且无限数量的记录(最多为计算机的物理内存)。然而,这两种方法,(1) 使用指针解析缓冲区,或 (2) 使用格式化输入函数涵盖了大多数情况。剩下的就是根据需要建立在该基础上。