在scanf中,如何忽略两个中的一个,而不是两个字符?

时间:2014-03-28 13:22:58

标签: c date scanf

我对C很新,我正在编写一些代码,允许用户以dd / mm / yyyy或dd-mm-yyyy的形式输入日期。我无法弄清楚如何让scanf忽略破折号或斜线,但不是两者。在输入斜杠后我需要代码识别负数。目前,该计划只是认为它是一个破折号并忽略它。

    int dd1, mm1, yyyy1;
    char str [3];
    scanf("%d %[-/] %d %[-/] %d", &dd1, str, &mm1, str, &yyyy1);

任何帮助将不胜感激!

4 个答案:

答案 0 :(得分:2)

你可以阅读输入并随后用sscanf()解析它 示例程序:

#include <stdio>
int main(void)
{
  char buf[256];
  int dd1, mm1, yyyy1;
  printf("input date: ");
  // this is normally a function for file operations, but if you define stdin,
  // the standard input, as file pointer it reads keyboard input
  fgets(buf, 256, stdin); 
  if(sscanf(buf, "%d-%d-%d", &dd1, &mm1, &yyyy1) != 3)
  {
    if(sscanf(buf, "%d/%d/%d", &dd1, &mm1, &yyyy1) != 3)
    {
      printf("Wrong format!\n");
    }
    else
    {
      printf("format with /\n");
    }
  }
  else
  {
    printf("Format with -\n");
  }
}

好的,首先你用fgets()阅读你的输入。它将从标准输入(这是你的键盘)最大读取255个字节并且安全地进入我称为&#34; buf&#34;的数组中。然后我们使用sscanf()来&#34;分析&#34;输入。 sscanf()返回成功分配的变量的数量,因为您希望从字符串中读取3个数字,每个返回值不同于3表示错误/错误输入。第一次sscanf()使用 - 和第二次sscanf()检查日期格式,并使用 /

检查日期格式

答案 1 :(得分:1)

阅读(潜在的恶意)用户输入的经典方式包括3个步骤:

  1. I / O
  2. 解析
  3. 范围验证。
  4. I / O:

    char buffer[100];
    if (fgets(buffer, sizeof buf, stdin) == NULL) Handle_EOForIOerror();
    

    解析

    int dd1, mm1, yyyy1;
    // check the result of `sscanf()`
    if (sscanf(buffer, "%2d/%2d/%4d", &dd1, &mm1, &yyyy1) != 3) ||
        sscanf(buffer, "%2d-%2d-%4d", &dd1, &mm1, &yyyy1) != 3)) FormatError();
    

    范围验证

    if (dd1 < 1 || dd1 > 31 || mm1 < 1 || mm1 > 12 || ...) Handle_RangeError();
    

    其他检查包括

    1)寻找尾随垃圾

    int n;
    if (sscanf(buffer, "%2d/%2d/%4d %n", &dd1, &mm1, &yyyy1, &n) != 3) ||
        sscanf(buffer, "%2d-%2d-%4d %n", &dd1, &mm1, &yyyy1, &n) != 3))FormatError();
    if (buffer[n] != '\0') TrailingJunkDetected();
    

    2)if (!valid_date(yyyy1, mm1, dd1)) ...

答案 2 :(得分:1)

对不起前一件事感到抱歉。

如果您不想使用中间存储空间,可以让您的程序说出“嘿!”首先,我想要一个数字,然后是一个字符,然后是另一个数字,然后是一个字符,最后是另一个数字。&#34;这正是你的日期字符串格式化的方式。

还有两个条件是,您希望两者之间的两个字符彼此相同,也可以是短划线或斜线。这就是你如何做到的:

int dd1, mm1, yyyy1;
char sep1, sep2;

if ( scanf( "%d%c%d%c%d", &dd1, &sep1, &mm1, &sep2, &yyyy1 ) == 5 && sep1 == sep2 && ( sep1 == '/' || sep1 == '-' ) )
    printf( "Day: %d\nMonth: %d\nYear: %d", dd1, mm1, yyyy1 );
else {
    // invalid syntax
}

正如我上面描述的那样,它要求一个整数,然后是字符,整数,字符和整数。如果它无法完成所有这些,那么它将不会返回5(scanf返回它填充的变量数)。接下来要检查两个分隔符是否相同;最后,分隔符必须是破折号或斜线。

只需将其作为替代方案,总会有另一种方法。

答案 3 :(得分:0)

你可以试试这个:

scanf(" %3d%1[/-]%3d%1[/-]%5d", ...);

因为这些字段宽度仅指定最大宽度,而不指定最小宽度,所以它们不能用于区分输入与窄字段。因此,如果您想严格限制输入格式为dd/mm/yyyydd-mm-yyyy,则需要在scanf()成功返回后进行更多语法检查。

这是一个测试程序:

#include <stdio.h>

int
main(void)
{
    int yy, mm, dd;
    char del1[2], del2[2];

    const char *dates[] = {
        /* well formed */
        "28-03-2014",
        "-28-03-2014",
        "28--03-2014",
        "28-03--2014",
        "28/03/2014",
        "-28/03/2014",
        "28/-03/2014",
        "28/03/-2014",
        /* ill formed */
        "28-3-2014",
        "8-03-2014",
        "28-03-14",
        "28-03/2014",
        "28/03-2014",
        "28 03 2014",
        NULL,
    };

    for (size_t i = 0; dates[i]; i++) {
        int ret = sscanf(dates[i], " %3d%1[/-]%3d%1[/-]%5d", &dd, del1, &mm, del2, &yy);
        if (ret != 5 || del1[0] != del2[0]) {
            fprintf(stderr, "%s : syntax error\n", dates[i]);
            continue;
        }
        printf("del = %c, yy = %d, mm = %d, dd = %d\n", del1[0], yy, mm, dd);
    }

    return 0;
}