fscanf读取字符串时出现问题

5

我正在读取一个 .txt 文件,使用 fscanf 以格式化的方式获取数据。我遇到问题的那一行是这样的:

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

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

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

然而,当我尝试读取名字时(没有空格),它就无法正常工作,并且会干扰其他fscanf的工作。但是,在我使用不同文件时,使用[^\n]可以正常工作。不确定发生了什么事情。

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

编辑//

好的,如果我使用:

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

这使我能够读取没有空格的字符串。但是当我得到一个带有空格的“名称”时,它无法正常工作。

你的文件里可能有多余的换行符吗? - Paul Tomblin
5个回答

13
这里面存在一个问题:
result = fscanf(fp, "%[^\n]s", ap->name);

你的格式说明符末尾多了一个额外的字符"s"。正确的格式说明符应该只是%[^\n],意思是“读取一个由非换行符组成的字符串”。多余的字符"s"不是格式说明符的一部分,所以它被解释为字面量:“读取输入中的下一个字符;如果它是“s”,则继续,否则失败。”
然而,这个额外的字符"s"并不会对你造成实际伤害。你确切地知道输入的下一个字符是什么:一个换行符。它不匹配,并且输入处理停止在那里,但这并不重要,因为它是你的格式说明符的结尾。但是,如果在同一个格式字符串中,在此之后还有其他格式说明符,则会出现问题。
真正的问题是你没有消耗掉换行符:你只读取了所有字符,直到换行符,但没有读取换行符本身。要解决这个问题,你应该这样做:
result = fscanf(fp, "%[^\n]%*c", ap->name);
%*c格式说明符表示读取一个字符(c),但是不将其赋值给任何变量(*)。如果省略了*,则必须向fscanf()传递另一个参数,该参数包含指向字符的指针(char*),fscanf()会将读取到的字符存储在其中。
您也可以使用%[^\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);

2

Jumm写道:

如果我在上面的fscanf位置使用fgets,那么我会在变量中得到“\n”。

这是一个更容易解决的问题,所以使用它:

fgets( ap->name, MAX, fp ) ;
nlptr = strrchr ( ap->name, '\n' ) ;
if( nlptr != 0 )
{
    *nlptr = '\0' ;
}

1
fgets() 没有问题,因为它的第二个参数设置了大小限制 - 你可能在想 gets()。 - anon
我删除了回复Neil评论的评论,因为我是错误的,并为在这里急于行动道歉...是的,确实你是正确的Neil...需要一杯咖啡... :) - t0mm13b
2
通过删除你的评论,你只是让Neil的评论看起来像是在批评我的帖子,而我不认为这是他的本意。 - Clifford

0

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


0

据我所知,在 fscanf 函数中没有你试图暗示的正则表达式,也没有在任何地方看到过 - 请 enlighten me。

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

但是千万不要使用标准老的 gets 家族函数,如 Clifford 的答案所指定的那样,因为那就是缓冲区溢出发生的地方,曾经在上世纪 90 年代的臭名昭著的 Morris 蠕虫中被使用,更具体地说是在调用 getsfingerd 守护进程中引起了混乱。幸运的是,现在已经被修补了。此外,许多程序员已经被钻石教导不要使用该函数。

即使微软公司也采用了一个安全版本的 gets 函数族,指定了一个参数来指示缓冲区的长度。

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

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

希望这能有所帮助, 最好的祝福, 汤姆。


1
错误 - 请看我对 Clifford 回答的评论。 - anon
这适用于单个字符串,但是对于带有空格的字符串则不行。不过还是谢谢提供额外的信息 =] - jumm

-1
我找到了问题。
正如Paul Tomblin所说,我在上面的字段中有一个额外的换行符。所以根据tommieb75的建议,我使用了以下内容:
result = fscanf(fp, "%s\n", ap->code);
result = fscanf(fp, "%[^\n]s", ap->name);

问题解决了!

感谢您的帮助。


如果您这样做,请确保ap->code和ap->name有足够的存储空间。 - Alok Singhal
此处的s没有意义。%[^\n]已经要求所有字符,但不包括换行符。 - alk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接