从生成的字符串中扫描
scanf from a generated string
我需要创建一个程序来逐行读取文件并在每一行中扫描一些数据。
例如在一行中:
# 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
我做错了什么?
您正在阅读输入中的“#”。输入中剩余的内容(包括最初的“2”)被读入缓冲区。
与输入流相反,您使用的字符串 line
在 sscanf(line,"%d",&nu_deadend);
之后仍然包含已经成功扫描的初始“2”,而不是在成功扫描时丢失它。
因此您尝试扫描“(...)”时需要考虑“2”。
例如喜欢
sscanf(line,"%*d (%d,%d)",&x,&y);
对于第一个“()”。
第二个要么需要更精细的忽略,要么需要在适当的索引处开始扫描字符串中的相关“(”。
由于您需要循环,因此精心忽略无济于事。您必须先搜索正确的“(”,然后进行扫描。
来自我的最高评论...
与从中断处继续的 scanf
不同,sscanf
将从给定的缓冲区开始。因此,您可能需要使用(例如)char *cp = line;
,然后使用并推进 cp
以指向下一个标记。
sscanf
不适合这个,因为它 return 是 count 而 not消耗的字节数。
最好使用 fgets
、cp
和 strtok
,并将 strtok
的 return 值传递给 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 (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
我做错了什么?
您正在阅读输入中的“#”。输入中剩余的内容(包括最初的“2”)被读入缓冲区。
与输入流相反,您使用的字符串 line
在 sscanf(line,"%d",&nu_deadend);
之后仍然包含已经成功扫描的初始“2”,而不是在成功扫描时丢失它。
因此您尝试扫描“(...)”时需要考虑“2”。
例如喜欢
sscanf(line,"%*d (%d,%d)",&x,&y);
对于第一个“()”。
第二个要么需要更精细的忽略,要么需要在适当的索引处开始扫描字符串中的相关“(”。
由于您需要循环,因此精心忽略无济于事。您必须先搜索正确的“(”,然后进行扫描。
来自我的最高评论...
与从中断处继续的 scanf
不同,sscanf
将从给定的缓冲区开始。因此,您可能需要使用(例如)char *cp = line;
,然后使用并推进 cp
以指向下一个标记。
sscanf
不适合这个,因为它 return 是 count 而 not消耗的字节数。
最好使用 fgets
、cp
和 strtok
,并将 strtok
的 return 值传递给 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