ANSI C:如何获取用户的输入然后反向打印?

ANSI C: How do you ingest input from user and then print it in reverse?

我正在尝试编写一个程序(针对 class),允许用户输入一个字符串,然后反向输出该字符串,直到用户输入“完成”为止, “完成”或“d”。

这是我当前的代码:

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

#define BUFFER_SIZE 50

int main(void) {

   char userText[BUFFER_SIZE];
   int i;
   int len;
   
   do
   {
      fgets(userText, BUFFER_SIZE, stdin);
      userText[(len = strcspn (userText, "\n"))] = 0;   
         
      for ( i = len - 1; i >= 0; i-- )
      {
         printf("%c", userText[i]);
      }
      printf("\n");
      
   } while ( ( strcmp(userText, "Done") != 0 ) && ( strcmp(userText, "done") != 0 ) && ( strcmp(userText, "d") != 0 ) );
   
   return 0;
}

如您所见,我使用了 fgets,因为我必须允许用户输入包含空格的字符串,然后我还清除了缓冲区以避免换行。这是我必须回答的问题:

这是我当前的输出:

你不是在循环中终止你的字符串。您在循环之前使用 len = strlen(userText); 正确地完成了它,但您并没有在循环本身中完成它。从标准输入读取后,您需要再次添加空终止符。

解决此问题的更好方法是使用中间测试循环而不是预测试循环。这样,您可以在代码中的 一个 位置而不是两个位置从 stdin 读取,从而消除代码重复。

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

int main(void) {
    char userText[50];
    int i;
    int len;

    for (;;) {
        fgets(userText, 50, stdin);
        len = strlen(userText);
        userText[len - 1] = '[=10=]';

        if ((strcmp(userText, "Done") == 0) || (strcmp(userText, "done") == 0) || (strcmp(userText, "d") == 0)) {
            break;
        }

        for (i = len - 1; i >= 0; i--) {
            printf("%c", userText[i]);
        }
        printf("\n");
    }
   
    return 0;
}

你给自己添麻烦了。一旦你有了长度,你就可以简单地循环那么多次,从结尾到开头输出字符,然后输出一个换行符。只需将所有这些都包含在一个循环中,以持续获取输入并检查您的 'd'"Done""done" 以打破循环。

(注意:,您可以简单地测试 len == 1 & *userText == 'd' 来处理 'd' 情况下的退出,而无需调用 strcmp() -- 最多你)

在查看解决方案之前,您应该避免在代码中使用 Magic-Numbers(例如 50)。相反:

...
#define MAXC 50     /* if you need a constant, #define one (or more) */

int main(void) {

    char userText[MAXC];
    ...
        if (!fgets (userText, MAXC, stdin))         /* validate EVERY user-input */
            return 1;

这样,如果您需要更改字符串的长度,您可以在一个方便的位置调整长度,而无需选择所有函数调用或循环限制来进行更改。

验证每个输入

无论您使用的是什么输入功能,您都无法正确使用它,除非您检查return以确定如果输入成功或失败。您在这里使用 fgets()(对您有好处!),但您仍然需要检查 return。 fgets() 将 return 指向已填充缓冲区的指针成功,或 NULL EOF 或流错误。因此,您只需确保 return 不是 NULL 即可验证字符是否保存在 userText 中,例如

        if (!fgets (userText, MAXC, stdin))         /* validate EVERY user-input */
            return 1;

您在第一次迭代中调用了未定义的行为,在len-1中:

    int len;
    ...
        userText[len-1] = '[=12=]';      

在第一次迭代中,len 未初始化,任何尝试使用具有自动存储持续时间的变量值而其值不确定都会导致未定义的行为。具体来说:

C11 Standard - 6.7.9 Initialization(p10) "If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate." and C11 Standard - J.2 Undefined Behavior "自动存储持续时间对象的值在不确定时使用(6.2.4,6.7.9,6.8)"。

fgets() 在填充的缓冲区中包含 '\n'

当您尝试 "d""Done""done"strcmp() 时,您永远不会匹配 "d""Done"、和 "done" 因为缓冲区中实际包含的是 "d\n""Done\n""done\n"。删除 '\n' 的一种简单而可靠的方法是使用 strcspn(),例如

        userText[strcspn (userText, "\n")] = 0;             /* trim \n */

你可以通过保存strcspn()的return来获得没有'\n'的行的长度,例如

    size_t len = 0;
    ...
        userText[(len = strcspn (userText, "\n"))] = 0;     /* trim \n, save len */

现在您的 strcmp() 检查将匹配。

要反向输出用户输入,只需循环 len 次输出字符,从最后一个到第一个。 while 循环提供了一种简单的迭代方式,例如

        while (len--)                               /* loop len times */
            putchar (userText[len]);                /* output char (end-to-start) */
        putchar ('\n');                             /* tidy up with newline */

(注意: 不需要 printf ("%c", ... 单个字符,这就是 putchar()fputc() 的作用。A好的编译器通常会为您进行优化,但最好能展示对输出如何发生的理解)

将其放在一起并为用户输入提供可选的 "user str: " 提示,并为输出提供可选的 "reversed: " 前缀(比让用户看着闪烁的光标想知道您的程序是否具有挂),你可以这样做:

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

#define MAXC 50     /* if you need a constant, #define one (or more) */

int main(void) {

    char userText[MAXC];
    size_t len = 0;
    
    for (;;) {  /* loop continually */
        fputs ("\nuser str: ", stdout);             /* prompt for input (optional) */
        if (!fgets (userText, MAXC, stdin))         /* validate EVERY user-input */
            return 1;
        
        userText[(len = strcspn (userText, "\n"))] = 0;     /* trim \n, save len */
        
        if ((len == 1 && *userText == 'd') ||       /* check for 'd' alone */
            strcmp(userText, "Done") == 0  ||       /* check for "Done" */
            strcmp(userText, "done") == 0) {        /* check for "done" */
            return 0;
        }
        
        fputs ("reversed: ", stdout);               /* prefix for output (optional) */
        while (len--)                               /* loop len times */
            putchar (userText[len]);                /* output char (end-to-start) */
        putchar ('\n');                             /* tidy up with newline */
    }
    
    return 0;
}

注意: return 0; 是 C99 forward 的默认设置,但由于您指定了 C89,因此有必要)

示例Use/Output

$ ./bin/outputrev

user str: Hello there
reversed: ereht olleH

user str: Hey
reversed: yeH

user str: done

或:

$ ./bin/outputrev

user str: My
reversed: yM

user str: dog
reversed: god

user str: has
reversed: sah

user str: fleas
reversed: saelf

user str: d

检查一下,如果您有任何其他问题,请告诉我。