使用fscanf读取制表符分隔的记录

时间:2018-11-28 05:17:47

标签: c scanf strtok

数据文件:

classpath 'io.fabric.tools:gradle:1.+'

读取代码:

Newton  30  United Kingdom  Scientist
Maxwell 25  United Kingdom  Mathematician
Edison  60  United States   Engineer

我编写了一个程序,使用#define MAX_NAME 50 #define MAX_COUNTRY 25 #define MAX_PROFILE 20 struct person { char *name; int age; char *country; char *profile; }; struct person pObj; pObj->name = (char *) malloc(sizeof(MAX_NAME)); pObj->country = (char *) malloc(sizeof(MAX_COUNTRY)); pObj->profile = (char *) malloc(sizeof(MAX_PROFILE)); fscanf(fPtr,"%s\t%d\t%s\t%s\n",pObj->name,&pObj->age,pObj->country,pObj->profile); 将制表符分隔的记录读取为结构。我可以通过fscanf()strtok()函数执行相同的操作。但是,如果我使用strsep(),则不得不使用strtok()函数来加载atoi()字段。但是我不想使用该age函数。因此,我只是使用atoi()直接从FILE流缓冲区读取age作为Integer。工作正常。但是对于某些记录,国家字段为空,如下所示。

fscanf()

当我阅读第二条记录时,Newton 30 United Kingdom Scientist Maxwell 25 Mathematician Edison 60 United States Engineer 不会在国家/地区字段中填充空字符串,而是已使用个人资料数据填充。我们了解fscanf()就是这样工作的。但是,即使文件中为空,也可以选择扫描国家/地区字段吗?我可以不使用年龄fscanf()来做到这一点吗?即按相应的类型读取字段,但不按字符串读取所有字段。

1 个答案:

答案 0 :(得分:2)

原始格式

%s转换规范会跳过输入中的任何空格(空白,制表符,换行符等),然后读取非空格直到下一个空格字符。格式字符串中出现的\t导致fscanf()跳过零个或多个空格字符(不仅仅是制表符)。

您有:

fscanf(fPtr,"%s\t%d\t%s\t%s\the n", pObj->name, pObj->age, pObj->country, pObj-profile);

您需要传递指向年龄的指针,并且需要在->pObj之间使用箭头profile(请进行编译的邮政编码;在此位置,这不会激发您的信心)是这样的错误):

fscanf(fPtr,"%s\t%d\t%s\t%s\the n", pObj->name, &pObj->age, pObj->country, pObj->profile);

给出第一行输入:

Newton  30  United Kingdom  Scientist

fscanf()Newton读入pObj->name30读入pObj->age,美国into国家and王国into {一般来说,{1}} pObj-> profile . fscanf()和家人对空白非常随意。大多数转化会跳过前导空白。

在分配了4个值之后,格式末尾有\the n"。该标签页跳过了KingdomScientist之间的空格,但是数据与he n不匹配,因此扫描停止了—并不是您更明智。

下一个操作将在此操作停止的地方进行,因此将为下一个pObj->name分配Scientist,然后pObj->age转换将失败,因为Maxwell不会代表一个整数。转换停止在该fscanf()上。

因此问题继续存在。您在问题中显示的代码无法获得您要求的输出。

如果您坚持必须使用fscanf(),则需要使用%24[^\t]之类的扫描集来读取国家/地区。但是您最好使用fgets()或POSIX函数getline()来读取整行输入,然后也许使用sscanf(),但更可能使用strcspn()或{{1} },从标准C(或者可能是strpbrk()或(最好是POSIX strtok()或Windows strtok_r(),或非标准strtok_s())将行拆分到选项卡上的字段中。请注意,strsep()等人并不关心字段之间的分隔符(在您的情况下为制表符)有多少重复;您不能将它们带空字段。您可以使用strtok_r()strcspn()strpbrk()来标识空白字段。


清理格式

格式字符串已修改为:

strsep()

这是行不通的,但现在可以对其进行调整,以使其起作用。

fscanf(fPtr,"%s\t%d\t%s\t%s\n", pObj->name, &pObj->age, pObj->country, pObj->profile);

Beware trailing white space in scanf() format strings。前导空格会跳过前几行留下的任何换行符,并跳过一行中的任何前导空格。 if (fscanf(fPtr," %49[^\t]\t%d\t%24[^\t]\t%19[^\n]", pObj->name, &pObj->age, pObj->country, pObj->profile) != 4) …handle a format error… 查找多达49个非制表符;该选项卡是可选的,并且匹配任何空格序列,但是第一个字符将是一个选项卡,除非名称太长。然后,它读取一个数字,更多可选的空格(它不必是制表符,但除非数据格式错误,否则它将是空白),然后再读取多达24个非制表符,又是空格(其中第一个字符将是除非存在格式问题,否则应为标签页),以及最多19个非标签页。除非存在格式问题,否则下一个字符应为换行符。