在字符串中读取的fscanf问题

在字符串中读取的fscanf问题,c,scanf,C,Scanf,我正在读一个.txt文件。我正在使用fscanf获取格式化后的数据。 我遇到的问题是: result = fscanf(fp, "%s", ap->name); 这是好的,直到我有一个空白的名字,例如:圣艾夫斯 所以我用这个在空白处读: result = fscanf(fp, "%[^\n]s", ap->name); 然而,当我试着读第一个名字(没有空格)时,它就是不起作用,并且弄乱了另一个fscanf 但是我使用[^\n]它在我使用的另一个文件中运行良好。不知道发生了什么 如

我正在读一个.txt文件。我正在使用fscanf获取格式化后的数据。 我遇到的问题是:

result = fscanf(fp, "%s", ap->name);
这是好的,直到我有一个空白的名字,例如:圣艾夫斯 所以我用这个在空白处读:

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);

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

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

Jumm写道:

如果我使用fgets代替上面的fscanf,则变量中会出现“\n”

这是一个更容易解决的问题,因此请继续:

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

据我所知,在
fscanf
函数中,没有一个正则表达式是不存在的,我也没有在任何地方看到过它——请告诉我这一点

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

但看在皮特的份上,不要使用Clifford在上面的回答中指定的标准旧的
get
家族函数,因为这是缓冲区溢出发生的地方,并且在20世纪90年代的一个臭名昭著的蠕虫中被使用过——莫里斯蠕虫,更具体地说是在
fingerd
守护进程中,该守护进程用于调用导致混乱的
get
。幸运的是,现在,这一问题已经得到解决。而且,许多程序员已经养成了不使用函数的心态

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

编辑 我的错-我没有意识到Clifford确实指定了输入的最大长度…哇!很抱歉克利福德的回答是正确的!克利福德的答案是+1

谢谢尼尔指出我的错误

希望这有帮助, 顺致敬意, 汤姆。

我发现了问题

正如Paul Tomblin所说,我在上面的字段中有一个额外的新行字符。所以用tommieb75说的话:

result = fscanf(fp, "%s\n", ap->code);
result = fscanf(fp, "%[^\n]s", ap->name);
这把它修好了

谢谢您的帮助。

这有一个问题:

result = fscanf(fp, "%[^\n]s", ap->name);
就是在格式说明符的末尾有一个额外的
s
。整个格式说明符应该是
%[^\n]
,表示“读入由非换行字符组成的字符串”。额外的
s
不是格式说明符的一部分,因此它被解释为文字:“从输入中读取下一个字符;如果是“s”,则继续,否则失败。”

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

真正的问题是你没有阅读新行:你只阅读到新行之前的所有字符,而不是新行本身。要解决此问题,您应该执行以下操作:

result = fscanf(fp, "%[^\n]%*c", ap->name);
result = fscanf(fp, "%255[^\n]%*c", ap->name);
%*c
说明符表示读入字符(
c
),但不将其分配给任何变量(
*
)。如果省略了
*
,则必须传递另一个参数
fscanf()
,该参数包含指向某个字符的指针(a
char*
),然后它将存储读取的结果字符

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

最后,还应指定最大长度以避免缓冲区溢出。您可以通过将缓冲区长度放置在
%
[
之间来完成此操作。例如,如果
ap->name
是256个字符的缓冲区,则应执行以下操作:

result = fscanf(fp, "%[^\n]%*c", ap->name);
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);

您的文件中是否可能有一个额外的换行符?fgets()没有问题,因为它的第二个参数设置了一个大小限制-您考虑的是gets().我删除了我的评论以回应Neil的评论,因为我是不正确的,并为在这里跳枪道歉…是的,确实你是对的Neil…需要一杯咖啡因饮料…:)删除你的评论,你只是让Neil的评论看起来像是对我帖子的批评,我认为这不是他的意图。错-请看我对Clifford回答的评论。这适用于单个字符串,但不适用于包含空格的字符串。感谢您提供的额外信息=]如果您这样做,请确保ap->code和ap->name有足够的存储空间。此外,
s
在这里没有意义<代码>%[^\n]已请求除新行以外的所有字符。