如何一次一个数字地使用scanf()扫描整数?

时间:2016-04-21 03:03:36

标签: c scanf

我想以mm / dd的形式扫描日期,所以我写了这段代码:

#include <stdio.h>

int main (void)
{
  int start_date[4];

  scanf("%d%d/%d%d", &start_date[0], &start_date[1], &start_date[2], &start_date[3]);

  printf("%d%d%d%d\n", start_date[0], start_date[1], start_date[2], start_date[3]);

  return 0;
}

但是当我输入以下代码时:

04/20

scanf()读取此内容

4-419644000

如何制作它以便当我打印出start_date中的每个元素时,我明白了:

0420

当我输入之前的输入时?

5 个答案:

答案 0 :(得分:5)

一次读取一位数

使用%1d

int n = scanf("%1d%1d/%1d%1d", &start_date[0], &start_date[1], &start_date[2], &start_date[3]);
if (n != 4) { …handle error… }

请注意,这将同时接受:

19/96

1 9/
9

6

作为有效输入。如果你需要数字是连续的,你必须更加努力。一般来说,通常最好使用fgets()(或POSIX's)读取一行 getline())然后使用sscanf()解析该行。您还可以考虑检查字符串的长度等。

出了什么问题?

顺便说一下,你说:

  

但是当我输入以下代码时:

04/20
     

scanf()读取此内容

     

4-419644000

这里发生的事情是第一个04读取%d;第二个%d失败,因为/与数字不匹配,因此没有任何内容写入&start_date[1](或其他两个项目),scanf()返回1.自此没有检查返回值,你没有意识到这个问题。请注意,检查应如我所示(n != 4,其中4是您希望转换的项目数)。检查EOF无法正常工作;文件上没有EOF,但转换失败。既然你打印了一个未初始化的变量,你获得的值是不确定的(行话中的'未定义的行为');一个较大的负数是合理的(任何其他任何值,或崩溃,或......)。事实上,你有三个不确定的数字在一起,其中只有第一个是负数。避免未定义的行为;始终检查输入操作是否成功 - 如果失败则不要使用结果。 (或者,至少在使用结果失败时要非常谨慎;你需要确定你知道发生了什么。)

答案 1 :(得分:3)

%d将消耗字符串&#34; 04&#34;,如果您想真正逐位输入,请尝试%c,然后将其调整为数字:

char start_data[4];
scanf("%c%c/%c%c", &start_date[0], &start_date[1], &start_date[2], &start_date[3]);
start_data[0] -= '0';
start_data[1] -= '0';
start_data[2] -= '0';
start_data[3] -= '0';

另一种方法,你可以输入mm和dd并对其进行算术运算:

int mm, dd;
scanf("%d/%d", &mm, &dd);
int m0 = mm % 10;
int m1 = mm / 10;
int d0 = dd % 10;
int d1 = dd / 10;

答案 2 :(得分:1)

我认为您应该使用strptime()代替。它专门用于解析标准日期格式,例如你的。只需将整个“单词”作为字符串阅读并将其提供给strptime()

答案 3 :(得分:1)

将数据类型更改为char [],因此您的代码如下所示

JButton continueButton = new JButton("Continue");
continueButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent a) {
        if(continueButton.isEnabled()){

            Student studentScreen = new Student();
            studentScreen.dispose();

            AddGrades addGradesScreen = new AddGrades(studentNameTextField.getText());
            addGradesScreen.setVisible(true);
        }
    }
});

如果要将每个字符的数据类型更改为整数,可以通过查看ASCII代码进行更改,然后使用ASCII&#39; 0&#39;

减去字符数。

答案 4 :(得分:1)

评论太长了,但你可以看到为什么会发生这种情况:

   0x080484a7 <+58>:    call   0x8048360 <__isoc99_scanf@plt>
   0x080484ac <+63>:    mov    0x2c(%esp),%ebx
   0x080484b0 <+67>:    mov    0x28(%esp),%ecx
   0x080484b4 <+71>:    mov    0x24(%esp),%edx
   0x080484b8 <+75>:    mov    0x20(%esp),%eax
   0x080484bc <+79>:    mov    %ebx,0x10(%esp)
   ...
End of assembler dump.
(gdb) break *main+79
Breakpoint 4 at 0x80484bc
(gdb) r
Starting program: /root/test/testcode
04/21
Breakpoint 4, 0x080484bc in main ()
(gdb) i r
eax            0x4      4
ecx            0x80484fb        134513915
edx            0xb7fff000       -1207963648
ebx            0xb7fcc000       -1208172544
esp            0xbffff720       0xbffff720
ebp            0xbffff758       0xbffff758
esi            0x0      0
edi            0x0      0
eip            0x80484bc        0x80484bc <main+79>
eflags         0x286    [ PF SF IF ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51
(gdb) c
Continuing.

4-1207963648134513915-1208172544

以下是发生的事情:

mov    0x2c(%esp),%ebx
mov    0x28(%esp),%ecx
mov    0x24(%esp),%edx
mov    0x20(%esp),%eax

这四个mov语句是printf()调用的准备。每个%d对应一个寄存器:eax ebx ecx edx
您可以看到它将四个字节移动到每个寄存器中。这就是你看到胡言乱语的原因。打印的每个%d都需要一个4字节的整数。

我在所有四个mov语句之后设置了一个断点,所以让我们看一下它们包含的内容:

eax            0x4              4
ecx            0x80484fb        134513915
edx            0xb7fff000       -1207963648
ebx            0xb7fcc000       -1208172544

这看起来并不正确,但它确实对应于堆栈中的内容:

0xbffff740:     0x00000004      0xb7fff000      0x080484fb      0xb7fcc000

这些是寄存器中的四个值 第一个实际上是你的04组合。它将04解释为在%d的范围内,这是我机器上的4字节整数。
由于你试图抓住四个%d,程序会继续,并打印出四个%d。换句话说,它打印出四个4字节整数。

发生这种情况的原因是Jonathan Leffler解释的内容:scanf将04吞噬到第一个%d,然后打破/
你仍然有一个%d%d%d%d的printf(),所以程序尽职地打印出剩余的4字节十进制值。由于scanf失败,剩余的3 %d包含堆栈中发生的任何事情。