我有一个包含关键字和整数的文本文件,并且可以访问文件流以解析此文件。
我可以通过做解析来解析它
while( fscanf(stream, "%s", word) != -1 )
获取文件中的每个单词和int以供我解析,但我遇到的问题是我无法检测到空行“\ n”,然后我需要检测某些内容。我可以看到\ n是%s未检测到的字符。如何修改fscanf以获取EOL字符?
答案 0 :(得分:1)
您可以使用fscanf
完成您想要做的事情,但是正确执行此操作所需的检查和验证的数量,与使用正确的面向行的输入函数,如fgets
。
使用fgets
(或POSIX getline
)检测空行不需要任何特殊内容,或者除了读取法线之外。例如,要使用fgets
阅读一行文字,您只需提供一个足够大小的缓冲区,然后拨打一个电话即可将'\n'
读入并包含buf
:
while (fgets (buf, BUFSZ, fp)) { /* read each line in file */
要检查该行是否为空行,只需检查buf
中的第一个字符是否为'\n'
字符,例如
if (*buf == '\n')
/* handle blank line */
或者,在正常情况下,您将通过获取长度并使用 nul-terminated \n'来移除尾随&{39; '\n'
>性格。在这种情况下,您只需检查长度是否为0
(删除后),例如
size_t len = strlen (buf); /* get buf length */
if (len && buf[len-1] == '\n') /* check last char is '\n' */
buf[--len] = 0; /* overwrite with nul-character */
(注意:如果最后一个字符不是'\n'
,您知道该行长于缓冲区并且该行中的字符仍然未读 - 并且将在下一行中读取调用fgets
,或者您已经到达文件的末尾,并在最后一行结束非POSIX行)
总而言之,使用fgets
识别空行并提供打印完整行的示例,即使行超过缓冲区长度,也可以执行以下操作:
#include <stdio.h>
#include <string.h>
#define BUFSZ 4096
int main (int argc, char **argv) {
size_t n = 1;
char buf[BUFSZ] = "";
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (fgets (buf, BUFSZ, fp)) { /* read each line in file */
size_t len = strlen (buf); /* get buf length */
if (len && buf[len-1] == '\n') /* check last char is '\n' */
buf[--len] = 0; /* overwrite with nul-character */
else { /* line too long or non-POSIX file end, handle as required */
printf ("line[%2zu] : %s\n", n, buf);
continue;
} /* output line (or "empty" if line was empty) */
printf ("line[%2zu] : %s\n", n++, len ? buf : "empty");
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
示例输入文件
$ cat ../dat/captnjack2.txt
This is a tale
Of Captain Jack Sparrow
A Pirate So Brave
On the Seven Seas.
示例使用/输出
$ ./bin/fgetsblankln ../dat/captnjack2.txt
line[ 1] : This is a tale
line[ 2] : empty
line[ 3] : Of Captain Jack Sparrow
line[ 4] : empty
line[ 5] : A Pirate So Brave
line[ 6] : empty
line[ 7] : On the Seven Seas.
那为什么每个人都推荐fgets
?
好吧,让我们看看fscanf
做同样的事情,我会让你成为法官。首先,fscanf
不会使用'\n'
格式说明符(默认情况下)或使用字符类时读取或包含尾随的"%s"
"%[^\n]"
(因为它被明确排除在外)。因此,您无法使用相同的格式字符串读取带有字符的(1)行和(2)没有字符的行。您可以阅读字符并fscanf
成功,或者您没有遇到匹配失败。
正如评论中提到的那样,如果输入缓冲区中的下一个字符是使用'\n'
(fgetc
的{{1}}字符,则必须预先检查 1}})然后将其放回输入缓冲区getc
,如果它不是。
进一步添加到ungetc
任务,您必须独立验证每个检查,放回并阅读整个过程中的每一步。这会导致大量检查以处理所有情况,并提供必要的所有检查以避免未定义的行为。
作为这些检查的一部分,您需要将读取的字符数限制为少于缓冲区中字符数的一个,同时捕获下一个字符以确定该行是否太长而不适合。需要进行额外的检查以处理(没有失败)最后一行上具有非POSIX行结尾的文件 - fscanf
处理的问题没有问题。
以下是与上述fgets
代码类似的实现。通过并理解为什么每个步骤都必要以及每个验证可以防止的步骤。您可以稍微重新排列,但它已被削减到接近最低限度。经过它后,应该清楚为什么fgets
是处理空行检查的首选方法(通常是面向行的输入)
fgets
使用/输出与上述相同。仔细看看,如果您有任何其他问题,请告诉我。