【问题标题】:Check new lines before format element scanf在格式元素 scanf 之前检查新行
【发布时间】:2021-06-01 22:29:33
【问题描述】:

所以,我想用fscanf读取两个整数变量中的一些输入,输入必须是

INTEGERONE SPACEINTEGER\n

例如,一个文件可能是这样的:

235 190\n

我想要做的是检查在第一个整数或第二个整数之前是否有任何新行,如果是这种情况则抛出异常:

所以基本上你对fscanf 的处理方式就是:

FILE *somefile;
[...]
int w, h;
if (fscanf(somefile, "%d%d") == EOF)
    return (1);

但此方法不提供检查整数之间是否有新行

(例如,如果有INTEGER NEW_LINE INTEGER 它仍然会读取第二个)

因为它在看到一个数字之前会跳过所有空格,所以我设法做的更接近但仍然是错误的:

FILE *somefile;
int w, h;
char check[2];
if (fscanf(somefile, "%d%c%d%c", &w, check, &h, check + 1) == EOF)
    return (-1);
if (check[0] != ' ' || check[1] != '\n')
    return (-1);

所以在我这样做之后,我意识到%d 之前的%c 只读取一个字符,所以如果两者之间有空格然后换行,它将继续错误地读取文件并且不会检测到错误。 现在我想知道是否有一种方法可以跳过所有空格,例如使用" %c" 时,同时知道我们是否跳过了\n。感谢您的帮助。

PS:我知道我们可以使用fgetsstrtol,但这太容易了,在这个项目中我只能使用fscanf 来解析文件。

【问题讨论】:

  • Read a whole line 首先。然后使用sscanf解析该行,并检查sscanf returns2
  • 由于fscanf() 不关心换行符等,你不能使用它。您需要使用 fgets() 或 POSIX getline() 读取该行,然后使用 sscanf() 检查格式。
  • scanf 很乐意丢弃所有空格以匹配%d,因此它对您没有用处。
  • 如果您只能使用fscanf(),您将被推入一条痛苦的道路。对于所需的输入格式,fscanf() 大多是正确工作的错误工具。您可以使用它,但您将不得不非常努力地工作,多次调用它,并且您可能需要使用ungetc() 作为处理的一部分。工作量将是可怕的!请参阅 William Pursell 的回答,并将对 getchar() 的每个调用替换为对 fscanf() 的调用以读取一个字符并进行测试以查看您是否获得了正确的字符(加上 ungetc() 以在必要时将其放回原处)。

标签: c stdio


【解决方案1】:

scanf 很高兴地丢弃前导空格以匹配%d 转换说明符,这使得这有点难以做到。但是您当然可以使用fgetc(或getchar)来验证一个字符,然后将其推回流中以供scanf 使用。比如:

$ cat a.c
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>

int
main(void)
{
        int c;
        int d[2];
        c = getchar();
        if( 0
                ||!( c == '+' || c == '-' || isdigit(c))
                || ( c != ungetc(c, stdin) )
                || ( scanf("%d", d) != 1 )
                || ( getchar() != ' ' )
                || ( (c = getchar()) == EOF )
                ||!( c == '+' || c == '-' || isdigit(c))
                || ( c != ungetc(c, stdin) )
                || ( scanf("%d", d + 1) != 1 )
                || ( getchar() != '\n' )
        ){
                fprintf(stderr, "invalid input!\n");
                return EXIT_FAILURE;
        }
        printf("Read %d %d\n", d[0], d[1]);

        return EXIT_SUCCESS;
}
$ gcc a.c
$ printf '5 28\n' | ./a.out
Read 5 28
$ printf '5  28\n' | ./a.out
invalid input!
$ printf ' 5 28\n' | ./a.out
invalid input!
$ printf ' 5 28' | ./a.out
invalid input!

【讨论】:

  • 如果 OP 的老师想要一个与此类似的解决方案,那就明确证明老师是一个虐待狂......好吧,不是一个很好的人。
【解决方案2】:

如果有办法跳过所有空格

我只能使用 fscanf 来解析文件。

让我们假设 ungetc() 是不允许的 - 这让事情变得更加困难。


"%d" 愉快地使用所有(0 个或更多)前导空格,包括 '\n',恕不另行通知。

首先使用fscanf("%[...]") 查找前导空格。 usual suspects" \t\n\r\f\v"

// Consume leading whitespaces 
// Return
// 1: \n or EOF found
// 0: not found
int EatLeadingWS(FILE *f) {
  int count;
  char space[2];
  while ((count = fscanf("%1[ \t\n\r\f\v]", space)) == 1) {
    if (space[0] == '\n') return 1;
  }
  return count == EOF;
}

替代方案:使用"%c"isspace(),但是我们需要一种方法来放回非空格。

然后查找"%d"之前的空格

int some_int;
if (EatLeadingWS(f) == 0) {
  int count;
  if ((count = fscanf("%d", &some_int)) == 1) { 
    printf("int %d found before a \\n\n", some_int);
  }
}
  

另一种方法是使用char buf[100]; fscanf("%99[^\n]", buf) 读取大部分行,然后使用sscanf(buf, "%d%1[ ]%d" .... 解析字符串

【讨论】:

  • 谢谢,抱歉回复晚了。只有一个问题:为什么你只使用 2 表示 int space[2] 而不是 int space 因为你只读取一个字符?
  • @Fayeure 使用fscanf("%1[ \t\n\r\f\v]", space),代码确实读取最多1个字符。然而"%[...]" 保存了该字符并追加一个空字符。因此(更正)char space[2]; 中需要 2 个
猜你喜欢
  • 2022-10-13
  • 2015-08-16
  • 2021-06-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多