【问题标题】:converting an entered 5 digit number into words将输入的 5 位数字转换为单词
【发布时间】:2021-09-16 20:28:01
【问题描述】:

我正在尝试编写一个程序来读取一个五位数的邮政编码,然后以单词的形式输出这些数字。该程序也只接受 5 位数字。因此,如果输入是 123,则输出将是“您输入了 3 位数字”并循环返回,直到用户输入了 5 位数字。一旦他们这样做,它将以word格式输出数字。因此,如果输入 12345,则输出将是一二三四五。这是我目前所拥有的:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <math.h>

int main(void) {
int ac, count = 0;

printf("Enter a 5 digit area code: ");
scanf("%d", &ac);


do {
    ac /= 10;
    ++count;

} while (ac != 0);

while (count != 5) {
        printf("You intered in %d digits.\n", count);
        printf("Enter a 5 digit area code: ");
        scanf(" %d", &ac);
}

while (count == 5) {


}

return(0);

}

有两个问题我需要帮助。第一个是只要输入一个没有五位数的数字,就正确输出一次,然后循环无限。如果我输入 123,输出将是“您输入了 3 位数字”,然后它会提示我输入另一个数字。如果我随后输入 12345,它将再次输出“您输入了 3 位数字”。我是循环新手,不知道这个问题来自哪里。

第二个问题只是从数字到单词的转换,我从来没有遇到过这个问题,我不知道从哪里开始。

【问题讨论】:

  • 我建议您将数字作为字符串读取和操作。从字符串中提取数字比从数字中提取数字更简单。无论哪种方式,一旦您提取了一个数字,然后开始使用带有 10 个案例的 switch 语句来打印单词中的数字。编写一个函数来执行此操作,然后为每个数字调用它。还有比这更聪明的方法,但这只是一个开始。
  • while (count != 5) 显然是错误的,因为count 在循环体内永远不会改变。因此,一旦进入循环,它将永远不会退出。您需要重新评估count。编写一个函数来做到这一点(看到这里关于将代码分解为函数的趋势?)。
  • 另外,第二个 while 循环不会将控制权传递回第一个循环,即使它们在循环体中有相同的变量。
  • 邮政编码 01027 有 5 位数字,但如果您将其读取为整数,它将是 1027,其中有 4。您必须将其读取为字符串。
  • 考虑只问一个问题(而不是两个),这样你就能得到一个有针对性的答案。

标签: c loops


【解决方案1】:

正如在 cmets 部分中已经指出的,您的代码中的逻辑不适用于以零开头的代码。

如果将代码存储为整数,则无法区分代码01234 和代码1234。因此,如果您希望能够区分这两个代码,您必须至少在开始时将数字读取为字符串,而不是数字:

char buffer[100];

//prompt user for input
printf("Enter a 5 digit area code: ");

//attempt to read one line of input
if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
{
    fprintf( stderr, "error reading input!\n" );
    exit( EXIT_FAILURE );
}

请注意,上面的代码还需要包含头文件stdlib.h

现在,您必须计算输入的字符数,并确认确实有 5。此外,您必须验证所有输入的字符都是数字。如果输入无效,则必须提供适当的错误消息并再次提示用户。这最好使用无限循环来完成,该循环会重复直到输入有效,此时会执行显式的break 语句,这将跳出无限循环。

通过创建指向字符串的指针数组可以轻松地将单个数字转换为单词,这样第 0th 元素指向字符串 "zero",第 1st 元素指向"one" 等:

const char * const digit_names[] = {
    "zero", "one", "two", "three", "four",
    "five", "six", "seven", "eight", "nine"
};

由于 ISO C 标准保证字符集中的所有数字都是连续的(无论您使用哪个字符集),您可以简单地从字符中减去 '0'(数字 0 的字符代码)代码以便将数字从字符代码转换为09 之间的实际数字。现在,您可以使用该数字索引到数组digit_names 以打印相应的单词。

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

int main( void )
{
    const char * const digit_names[] = {
        "zero", "one", "two", "three", "four",
        "five", "six", "seven", "eight", "nine"
    };

    char buffer[100];

    for (;;) //infinite loop, equivalent to while(1)
    {
        int i;
        char *p;

        //prompt user for input
        printf("Enter a 5 digit area code: ");

        //attempt to read one line of input
        if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
        {
            fprintf( stderr, "Unrecoverable error reading input!\n" );
            exit( EXIT_FAILURE );
        }

        //verify that entire line of input was read in
        p = strchr( buffer, '\n' );
        if ( p == NULL )
        {
            fprintf( stderr, "Unrecoverable error: Line too long!\n" );
            exit( EXIT_FAILURE );
        }

        //remove newline character
        *p = '\0';

        //verify that exactly 5 characters were entered and that
        //each characters is a digit
        for ( i = 0; i < 5; i++ )
        {
            //verify that we are not yet at end of line
            if ( buffer[i] == '\0' )
            {
                printf( "Too few characters!\n" );

                //we cannot use "continue" here, because that would apply to
                //the innermost loop, but we want to continue to the next
                //iteration of the outer loop
                goto continue_outer_loop;
            }

            //verify that character is digit
            if ( !isdigit( (unsigned char)buffer[i] ) )
            {
                printf( "Only digits allowed!\n" );

                //we cannot use "continue" here, because that would apply to
                //the innermost loop, but we want to continue to the next
                //iteration of the outer loop
                goto continue_outer_loop;
            }
        }

        //verify that we are now at end of line
        if ( buffer[i] != '\0' )
        {
            printf( "Too many characters!\n" );
            continue;
        }

        //everything is ok with user input, so we can break loop
        break;

    continue_outer_loop:
        continue;
    }

    printf( "You entered this valid input: %s\n", buffer );

    printf( "In words, that is: " );

    for ( int i = 0; i < 5; i++ )
    {
        //don't print space on first iteration
        if ( i != 0 )
            putchar( ' ' );

        //casting to "unsigned char" is necessary to prevent
        //negative character codes
        fputs( digit_names[ ((unsigned char)buffer[i]) - '0' ], stdout );
    }

    printf( "\n" );
}

这是程序的一些示例输入和输出:

Enter a 5 digit area code: 1234
Too few characters!
Enter a 5 digit area code: 123456
Too many characters!
Enter a 5 digit area code: 12h45
Only digits allowed!
Enter a 5 digit area code: 12345
You entered this valid input: 12345
In words, that is: one two three four five

此外,如您所见,以0 开头的代码也可以:

Enter a 5 digit area code: 1234
Too few characters!
Enter a 5 digit area code: 01234
You entered this valid input: 01234
In words, that is: zero one two three four

【讨论】:

    【解决方案2】:

    对于第一个问题,如果数字不是 5 位,则第二个 while 循环条件始终评估为 false。每次获得新号码时都必须更新count,这意味着您必须重复do ... while 循环。在这种情况下,一个函数会很棒,永远不要忘记检查 scanf() 的返回值是唯一知道转换成功的(你扫描了一个数字)。

    你的函数可以是这样的,

    int count_digits(int num)
    {
      int digits = 0;
    
      do
      {
        digits++;
        num /= 10;
      } while (num != 0);
    
      return digits;
    }
    

    还有你更新的main()

    int main(void)
    {
      int ac = 0, count = 0;
    
      printf("Enter a 5 digit area code: ");
      if (scanf("%d", &ac) != 1) /* you read one int */
      {
        /* conversion error - maybe display error message */
        exit(EXIT_FAILURE);
      }
    
      count = count_digits(ac);
    
      while (count != 5)
      {
        printf("You intered in %d digits.\n", count);
        printf("Enter a 5 digit area code: ");
        if (scanf(" %d", &ac) != 1)
        {
          exit(EXIT_FAILURE); /* include <stdlib.h> for exit */
        }
    
        count = count_digits(ac);
      }
    }
    

    至于你问题的第二部分,不清楚你是否想对数字进行一些操作。如果您需要将其作为 int 格式,您可以使用 snprintf() 和足够大的缓冲区将其转换为字符串。

      char buff[100] = "";
    
      snprintf(buff, sizeof(buff), "%d", ac);
    

    如果您不需要对数字执行任何操作,您可以立即将其读取为字符串,使用fgets()

      char buff[100] = "";
    
      fgets(buff, sizeof(buff), stdin);
    

    (这也适用于以 0 开头的邮政编码)

    【讨论】:

      【解决方案3】:

      由于ZIP codes 可以以'0' 开头,因此使用scan("%d",... 会丢失前导0 信息。

      代码需要检测数字而不仅仅是 int 以禁止输入,如 "123\n""000123\n""+1234\n""12345xyz\n",但允许 "000001\n"" 000001\n""000001 \n"、@ 987654332@.

      下面使用"%n"记录扫描的偏移量。

        char buf[100];
        if (fgets(buf, sizeof buf, stdin)) {
          int left, right, end;
          long zip;
          if (sscanf(buf, " %n%6ld%n %n", &left, &zip, &right, &end) != 1) {
            puts("Non-numeric input");
          } else if (right - left != 5 || !isdigit((unsigned char ) buf[left])) {
            puts("Not 5 digits");
          } else if (buf[end] != '\0') {
            puts("extra junk at the end");
          } else {
            printf("Success %05ld\n", zip);
          }
        }
      

      替代代码也可能希望禁止前导/尾随空格。


      " %n%6ld%n %n"详情

      " ":使用可选的空白。

      "%n"scan 的记录偏移量

      "%6ld" 最多读取 6 个字符以形成 long - 无需更多(并防止溢出)。由于int 可能是 16 位,请使用 long 代替 00000-99999

      【讨论】:

        【解决方案4】:

        有时它有助于简化。 在这种情况下,消除一个 while 循环并在剩余循环中使用辅助函数会有所帮助。以下使代码能够迭代,直到通过单个循环输入正确的输入。 (注意,示例不包含错误处理,只是概念说明。)

        主要:

        int main(void)
        {
            int num;
            char buf[20]={0};
            printf("Enter a 5 digit number:");
            while (!isNumeric(buf))//helper function 
            {
                printf("try again\n:");
                scanf("%s", buf); 
            }
            //convert if needed
            num = atoi(buf);
        
            printf("%s\n%d", buf, num);
            
            return 0;
        }
        

        辅助函数:

        bool isNumeric(char *number)
        {
            if(strlen(number) != 5) return false;
            
            while(*number)
            {
                if(!isdigit(*number))
                    return false;
                number++;
            }
            
            return true;
        }
        

        【讨论】:

        • 根据§7.4 ¶1 of the ISO C standard,如果*number 为负数,函数调用!isdigit(*number) 可能会调用未定义的行为。正确的写法是!isdigit( (unsigned char)*number )。请参阅this answer to another question 了解其背后的原因。
        • 在用户输入任何内容之前,在初始提示中打印“重试”似乎不合适。
        • 函数atoishould generally not be used,因为它在范围错误上调用未定义的行为(请参阅§7.22.1 ¶1 of the ISO C standard)。应该改用函数strtol。即使在这种情况下可以排除范围错误,我也不确定推荐使用函数atoi 是否是个好建议。
        • @AndreasWenzel - 首先,短语 try again 在初始提示期间不会出现。接下来,标准库中的大多数 函数调用未定义的行为根据其设计未使用时。但是您已经知道这一点,我想知道您引用它们的动机。此外,我在这里使用它们来保持我建议的概念整洁。您可以尝试根据自己的答案执行此操作。建议使用 gotocontinue 是合法的语法,您可以选择使用它们,但大多数 C 编程指令通常不鼓励使用它们。
        • 在您之前的评论中,您写道:"First,, the phrase try again does not occur during the initial prompt."。这种说法是不正确的。它在初始提示上打印"Enter a 5 digit number:try again"。请参阅 this link 以获取程序执行的演示。
        猜你喜欢
        • 2020-01-28
        • 2019-04-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多