用C中的空格分割每行

时间:2019-08-27 20:50:18

标签: c file parsing

我有一个文件,其行格式为:

<string><spaces><string><spaces><string>

我不知道每个字符串之间的空格数。我想解析该行并将每个字符串插入一个变量(第一个字符串代表一个名称,第二个姓氏和第三个id)。我看到我可以使用strtok,但我不想使用它,而是使用循环遍历该行的循环。

我还发现我可以使用:

if(fscanf(party_data,"%s %s %s",name,last,id) != 3){
   break;
}

但是我认为使用while循环会更好。 while循环的问题是我不知道每个字符串之间的空格数量。我的目标是创建一个函数parseLine,该函数将获取三个指针(名称,姓氏和ID)并解析该行。该函数应如何显示?

2 个答案:

答案 0 :(得分:2)

scanf格式字符串中的单个空格字符(或其堂兄,例如fscanfsscanfvfscanf等)可以匹配任意数量输入中的空白(不仅包括空格,还包括制表符,垂直制表符和换行符),因此您的fscanf调用现在可能还不错。哦,除了一个细节之外:您通常希望避免进行简单的%s转换,而应使用类似以下内容的方法:

char dest[16];
scanf("%15s", dest);

也就是说,您总是要指定最大大小,该大小应该比要提供的缓冲区的大小小一个。

如果您不想使用scanf和公司,则有两种选择。您可以以strspnstrcspn开头,也可以只使用isspace的while循环:

char *line = /* whatever*/;

while (!isspace(*line))
   *first++ = *line++;
*first = '\0';

while (isspace(*line))
    ++line;

while (*isspace(*line))
    *second++ = *line++;
*second = '\0';

while (isspace(*line))
    ++line;

while (*isspace(*line))
    *third++ = *line++;
*third = '\0';

在实际使用中,您还希望跟踪目标缓冲区的长度,并且仅将其实际可容纳的数量复制到该缓冲区中(或者计算出每种需要的大小并相应地分配)。

哦,还有其他一些次要细节:调用isspace时,应该将其操作数真正转换为unsigned char。如果不进行强制转换,则将其用于某些非英语字符(例如带有重音符号,反引号等)可能会产生不确定的行为。

答案 1 :(得分:1)

这是您必须使用任何一种语言进行的最基本操作之一:

  1. 从文件中读取数据,并且
  2. 将数据解析为所需的信息。

在您的情况下,输入文件中包含空格分隔的名称和ID。尽管您可以直接使用fscanf,但它非常脆弱。如果一行与您的 format字符串不匹配,则读取将失败,并出现 matching 失败,流中的字符提取将停止,然后剩下其余的输入缓冲区中要处理的行,然后才能继续操作。

因此,一种更好的方法是使用fgets和足够大小的缓冲区(或使用POSIX getline)将每一行读入缓冲区,以在每次读取时消耗整行输入。您可以从存储在缓冲区中的行中解析所需的信息,而不会影响您的读取操作。这还提供了能够独立验证您的(1)阅读和(2)信息解析的好处。

有很多方法可以从缓冲区解析所需的信息。您可以使用sscanf从缓冲区中读取(就像您在输入本身上使用fscanf一样),可以在缓冲区中移动一对指针,将每个单词放在括号中,然后memcpy nul-terminate ,您可以使用strtok(但它会修改原始缓冲区),也可以结合使用strspn和{{1 }}将每个单词括起来,类似于遍历指针。

在您的情况下,我们只使用strcspn,因为对于固定格式,这同样容易。要存储价值sscanf的3串字符串,使用这些成员创建一个结构,然后可以创建一个结构数组(我们将保留动态数组或链接列表供以后使用),然后可以存储所有您阅读的名称和ID,例如:

name, last, id

您现在有了一个结构体(使用方便的#include <stdio.h> #define MAXID 16 /* if you need a constant, #define one (or more) */ #define MAXNM 32 #define MAXPN 128 #define MAXC 1024 typedef struct { char name[MAXNM], last[MAXNM], id[MAXID]; } typeperson; typedef,您可以用来创建结构体数组(每个数组都初始化为全零),例如

typeperson

您现在可以填充一组int main (int argc, char **argv) { char buf[MAXC]; size_t n = 0; typeperson person[MAXPN] = {{"", "", ""}}; MAXPN)人。现在,只需使用作为程序第一个参数提供的名称打开文件(或者,如果没有给出参数,则默认从128读取),然后 validate 文件已打开以供阅读:

stdin

打开文件并 已验证 ,您现在可以将每一行读入 /* use filename provided as 1st argument (stdin by default) */ FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin; if (!fp) { /* validate file open for reading */ perror ("file open failed"); return 1; } ,然后从buf解析name, last, id使用buf(除sscanf"%c"以外的所有转换说明符(技术上为"%[..]",但它不是从缓冲区提取的)跳过所有前导空格,使您可以分隔您的"%n",无论它们之间有多少空白:

name, last, id

注意 /* protect array bounds and read each line into struct */ while (n < MAXPN && fgets (buf, MAXC, fp)) { if (sscanf (buf, "%s %s %s", person[n].name, person[n].last, person[n].id) == 3) n++; } 的测试可以保护数组边界,并防止您编写超出存储容量的元素)

如果该行的格式错误,该怎么办?您如何恢复?简单。通过在每次读取时占用一行,任何与您的n < MAXPN 格式字符串不匹配的行都会被忽略,不会给您造成任何问题。

剩下的就是关闭文件并以所需的任何方式使用数据。将其放到一个简短的示例中,您可以这样做:

sscanf

示例输入文件

使用与格式不匹配的故意行(例如#include <stdio.h> #define MAXID 16 /* if you need a constant, #define one (or more) */ #define MAXNM 32 #define MAXPN 128 #define MAXC 1024 typedef struct { char name[MAXNM], last[MAXNM], id[MAXID]; } typeperson; int main (int argc, char **argv) { char buf[MAXC]; size_t n = 0; typeperson person[MAXPN] = {{"", "", ""}}; /* use filename provided as 1st argument (stdin by default) */ FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin; if (!fp) { /* validate file open for reading */ perror ("file open failed"); return 1; } /* protect array bounds and read each line into struct */ while (n < MAXPN && fgets (buf, MAXC, fp)) { if (sscanf (buf, "%s %s %s", person[n].name, person[n].last, person[n].id) == 3) n++; } if (fp != stdin) fclose (fp); /* close file if not stdin */ for (size_t i = 0; i < n; i++) /* output the resutls */ printf ("person[%3zu] : %-20s %-20s %s\n", i, person[i].name, person[i].last, person[i].id); } ):

"..."

使用/输出示例

$ cat dat/peopleid.txt
George      Washington          1
John        Adams               2
Thomas      Jefferson           3
James       Madison             4
...
Royal       Embarrasment        45

仔细检查一下,如果还有其他问题,请告诉我。