5

我正在阅读 .txt 文件。我正在使用 fscanf 来获取格式化的数据。我遇到的问题是这样的:

result = fscanf(fp, "%s", ap->name);

这很好,直到我有一个带有空格的名字,例如:St Ives 所以我用它来读取空格:

result = fscanf(fp, "%[^\n]s", ap->name);

但是,当我尝试读取名字(没有空格)时,它就不起作用并弄乱了另一个 fscanf。

但我使用 [^\n] 它在我正在使用的不同文件中工作正常。不知道发生了什么。

如果我使用 fgets 代替上面的 fscanf,我会在变量中得到“\n”。

编辑//

好的,所以如果我使用:

result = fscanf(fp, "%s", ap->name);
result = fscanf(fp, "%[^\n]s", ap->name);

这使我可以读取没有空格的字符串。但是当我得到一个带有空格的“名称”时,它就不起作用了。

4

5 回答 5

13

这样做的一个问题:

result = fscanf(fp, "%[^\n]s", ap->name);

s您的格式说明符末尾有一个额外的内容。整个格式说明符应该只是%[^\n],它表示“读取由非换行符组成的字符串”。extras不是格式说明符的一部分,因此它被解释为文字:“从输入中读取下一个字符;如果是“s”,则继续,否则失败。”

不过,额外的东西s实际上并没有伤害到你。您确切地知道输入的下一个字符是什么:换行符。它不匹配,输入处理在那里停止,但这并不重要,因为它是格式说明符的结尾。但是,如果在同一格式字符串中此格式说明符之后还有其他格式说明符,这将导致问题。

真正的问题是您没有使用换行符:您只是在读取换行符之前的所有字符,而不是换行符本身。要解决这个问题,你应该这样做:

result = fscanf(fp, "%[^\n]%*c", ap->name);

说明%*c符表示要读入一个字符 ( c),但不要将它分配给任何变量 ( *)。如果省略*,则必须传递fscanf()另一个包含指向字符 (a char*) 的指针的参数,然后它将存储读取的结果字符。

您也可以使用%[^\n]\n, 但这也会读取换行符后面的任何空格,这可能不是您想要的。当fscanf在其格式说明符(空格、换行符或制表符)中找到空格时,它会消耗尽可能多的空格(即您可以认为它消耗与正则表达式匹配的最长字符串[ \t\n]*)。

最后,您还应该指定最大长度以避免缓冲区溢出。您可以通过将缓冲区长度放在%和之间来做到这一点[。例如,如果ap->name是 256 个字符的缓冲区,您应该这样做:

result = fscanf(fp, "%255[^\n]%*c", ap->name);

这对于静态分配的数组非常有用;不幸的是,如果数组在运行时动态调整大小,则没有简单的方法将缓冲区大小传递给fscanf. 您必须使用创建格式字符串sprintf,例如:

char format[256];
snprintf(format, sizeof(format), "%%%d[^\n]%%*c", buffer_size - 1);
result = fscanf(fp, format, ap->name);
于 2009-12-12T16:03:19.250 回答
2

朱姆写道:

如果我使用 fgets 代替上面的 fscanf,我会在变量中得到“\n”。

这是一个更容易解决的问题,所以去吧:

fgets( ap->name, MAX, fp ) ;
nlptr = strrchr ( ap->name, '\n' ) ;
if( nlptr != 0 )
{
    *nlptr = '\0' ;
}
于 2009-12-12T14:58:37.933 回答
0

我不确定您的意思是 [^\n] 应该如何工作。[] 是一个修饰符,表示“接受一个字符,除了该块内的任何字符”。^反转条件。带有 fscanf 的 %s 只读取直到遇到分隔符。对于其中包含空格和换行符的字符串,请改用 fgets 和 sscanf 的组合,并指定长度限制。

于 2009-12-12T14:55:21.950 回答
0

据我所知,没有任何事情你试图在fscanf函数中暗示一个不存在的正则表达式,据我所知,我也没有在任何地方看到它 - 请启发我。

读取字符串的格式说明符是%s,可能是您需要这样做,%s\n这将获取换行符。

但是看在皮特的份上,不要使用上面 Clifford 的回答所指定gets的标准旧家族函数,因为那是缓冲区溢出发生的地方,并被用于 1990 年代臭名昭著的蠕虫——莫里斯蠕虫,更具体地说是在守护进程中,它曾经调用这造成了混乱。幸运的是,现在,它已经被修补了。而且,很多程序员都被灌输了不使用该功能的心态。fingerdgets

甚至微软也采用了安全版本的gets函数系列,它指定了一个参数来指示缓冲区的长度。

编辑 我的错 - 我没有意识到克利福德确实已经指定了输入的最大长度......哎呀!对不起!克利福德的答案是正确的!所以 +1 克利福德的回答。

感谢尼尔指出我的错误......

希望这会有所帮助,最好的问候,汤姆。

于 2009-12-12T15:09:33.950 回答
-1

我发现了问题。

正如 Paul Tomblin 所说,我在上面的字段中多了一个换行符。因此,使用 tommieb75 所说的我使用了:

result = fscanf(fp, "%s\n", ap->code);
result = fscanf(fp, "%[^\n]s", ap->name);

这解决了它!

谢谢你的帮助。

于 2009-12-12T15:43:17.220 回答