【问题标题】:scanf from a generated stringscanf 从生成的字符串
【发布时间】:2022-01-22 08:29:20
【问题描述】:

我需要创建一个程序来逐行读取文件并在每一行扫描一些数据。 例如在一行中:

# 2 (x1,y1)(x2,y2)

我需要 x1,y1 和 x2,y2 我的代码是

    char firstCharacter;
    char line[100];
    scanf("%c",&firstCharacter);
    while ((fgets(line, sizeof line, stdin) != NULL) && (line[0] != '\n')){
        if(firstCharacter == '#'){
            int nu_deadend;
            sscanf(line,"%d",&nu_deadend);
            for (int i = 0; i < nu_deadend; i++) {
                int x,y;
                sscanf(line,"(%d,%d)",&x,&y);
                printf("x: %d y: %d\n",x,y);
            }
        }
    }
    return 0;

但来自输入:

# 2 (2,3)(3,4)

它输出:

x:0 y:0
x:0 y:0

预期输出:

x:2 y:3
x:3 y:4

我做错了什么?

【问题讨论】:

  • 请说明您的代码忽略的scanf的所有返回值。
  • scanf 不同,sscanf 会从它给定的缓冲区开始。因此,您可能需要使用(例如)char *cp = line;,然后使用并前进cp 以指向下一个令牌。 sscanf 不适合这个。最好使用fgetscpstrtok 并将strtok 的返回值传递给sscanf 此外,您永远不会为第二行重置firstCharacter(即我假设每一行都以@987654338 开头@)

标签: c string scanf


【解决方案1】:

来自我的顶级评论...

scanf 不同,sscanf 会从它给定的缓冲区开始。因此,您可能需要使用(例如)char *cp = line;,然后使用并前进cp 以指向下一个令牌。

sscanf 不适合这个,因为它返回一个 count不是消耗的字节数。

最好使用fgetscpstrtok并将strtok的返回值传递给sscanf

另外,您永远不会为第二行重置 firstCharacter(即我假设每一行都以 # 开头)


这是一个重构版本。有注释:

#include <stdio.h>
#include <string.h>

#ifdef DEBUG
#define dbgprt(_fmt...)         printf(_fmt)
#else
#define dbgprt(_fmt...)         do { } while (0)
#endif

int
main(void)
{
    char *tok;
    char line[100];

    // FORMAT:
    //  # <count> (x,y)(x,y)...
    while (fgets(line, sizeof line, stdin) != NULL) {
        dbgprt("LINE: %s",line);

        // get first token
        tok = strtok(line," \n");
        if (tok == NULL)
            break;

        // this must be "#"
        if (*tok != '#')
            continue;

        // get the count
        int nu_deadend;
        tok = strtok(NULL," \n");
        sscanf(tok, "%d", &nu_deadend);
        dbgprt("NUM: %d\n",nu_deadend);

        // get "(x,y)(x,y)(x,y)"
        tok = strtok(NULL," \n");

        for (int i = 0; i < nu_deadend; i++) {
            int x, y;

            dbgprt("TOK: '%s'\n",tok);
            sscanf(tok, "(%d,%d)", &x, &y);
            printf("x: %d y: %d\n", x, y);

            // advance past the end of the current (x,y)
            tok = strchr(tok,')');
            if (tok == NULL)
                break;
            ++tok;
        }
    }

    return 0;
}

这是我使用的测试输入:

# 2 (2,3)(3,4)
# 3 (1,7)(9,2)(8,3)

这是调试输出:

LINE: # 2 (2,3)(3,4)
NUM: 2
TOK: '(2,3)(3,4)'
x: 2 y: 3
TOK: '(3,4)'
x: 3 y: 4
LINE: # 3 (1,7)(9,2)(8,3)
NUM: 3
TOK: '(1,7)(9,2)(8,3)'
x: 1 y: 7
TOK: '(9,2)(8,3)'
x: 9 y: 2
TOK: '(8,3)'
x: 8 y: 3

而且,这里是干净的输出:

x: 2 y: 3
x: 3 y: 4
x: 1 y: 7
x: 9 y: 2
x: 8 y: 3

【讨论】:

    【解决方案2】:

    您正在从输入中读取“#”。输入中剩下的内容(包括最初的“2”)被读入缓冲区。

    与输入流相比,您使用的字符串linesscanf(line,"%d",&amp;nu_deadend); 之后仍然包含已成功扫描的初始“2”,而不是在成功扫描时丢失它。

    因此,您尝试扫描“(...)”需要考虑“2”。

    例如喜欢

    sscanf(line,"%*d (%d,%d)",&x,&y);
    

    对于第一个“()”。

    第二个需要更精细的忽略,或者需要在适当的索引处开始扫描字符串中相关的“(”。
    由于您需要循环,因此精心忽略无济于事。您必须首先搜索正确的“(”,然后进行扫描。

    【讨论】:

    • “除了输入流”是什么意思?
    • 我编辑了。现在清楚了吗? @kirjosieppo
    • 好的,谢谢!
    • 感谢您的回复,我实际上是 c 的新手。如何更改索引以开始扫描?
    • 如果您知道相应“(”的索引,您可以将其添加到line。有关详细信息,请单独提问。最好先检查现有的类似问题。
    猜你喜欢
    • 2015-03-05
    • 2011-12-02
    • 2016-02-22
    • 2011-05-27
    • 2014-01-15
    • 2012-08-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多