【问题标题】:ANSI C: How do you ingest input from user and then print it in reverse?ANSI C:你如何从用户那里获取输入然后反向打印?
【发布时间】:2021-05-01 20:27:34
【问题描述】:

我正在尝试编写一个程序(用于类),该程序允许用户输入一个字符串,然后反向输出该字符串,直到用户输入“完成”、“完成”或“ 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 是因为我必须允许用户输入包含空格的字符串,然后我还清除了缓冲区以避免换行。这是我必须回答的问题:

这是我当前的输出:

【问题讨论】:

  • 请阅读How to Ask。你有一个有趣的工作陈述,但我没有看到任何问题。
  • 每次调用fgets后,您都不会更改len
  • 考虑使用do-while 循环,这样您就不必在循环之前和循环期间复制fgets 内容。
  • 您的循环终止逻辑已损坏。
  • do-while 循环将导致打印“enod”(“完成”反转),因为打印发生在 do-while 的测试之前。我的答案通过使用中间测试循环解决了这个问题:stackoverflow.com/a/65929023/14430952

标签: arrays c while-loop nested-loops


【解决方案1】:

你让自己有点困难。一旦你有了长度,你可以简单地循环多次,从结尾输出字符到开头,然后输出换行符。只需将所有内容都包含在一个循环中,以不断获取输入并检查您的 'd'"Done""done" 以打破循环。

注意:,您可以简单地测试len == 1 &amp; *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;

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

验证每个输入

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

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

您在第一次迭代中使用len-1 调用未定义行为

    int len;
    ...
        userText[len-1] = '\0';      

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

C11 Standard - 6.7.9 Initialization(p10) "如果一个具有自动存储时长的对象没有被显式初始化,它的值是不确定的。" and C11 Standard - J.2 Undefined Behavior"一个具有自动存储时长的对象的值在不确定时使用(6.2.4、6.7.9、6.8)。"

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

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

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

你可以通过简单地保存strcspn()的返回来获得没有'\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() 的用途。一个好的编译器通常会为您进行优化,但是最好展示对输出如何发生的理解)

把它放在一起并为用户输入提供可选的"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 转发的默认值,但由于您指定了 C89,所以它是必需的)

使用/输出示例

$ ./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

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

【讨论】:

  • 这非常有帮助,但我确实有几个问题。首先,由于我们没有在课堂上学习,我无法使用您描述的某些方法,也就是说,我能够进行一些更改,并根据您所说的得到 99% 的解决方案。我现在唯一遇到的问题是程序退出之后是反转退出语句“完成”、“完成”或“d”。我能够摆脱所有额外的空格和新行,程序不再是无限的。因为这里放不下所有更新的代码,所以更新上面的问题。
  • 继续:我没有更新以使用 putchar 或 addputs,因为我目前不允许使用它们。我的最后一个问题:为什么这种格式适用于 strscspn?当我查找它时,给出的格式是:len = strcspn(str1, str2);所以只是想知道你的格式的逻辑以及为什么它在这种情况下有效?我知道strscspn本身就是确定str1减去str2的长度,但是为什么开头的userText是为什么=0是必要的呢?
  • strcspn (string, reject) 返回string 中的字符数,直到找到reject 中的第一个字符为止。所以strcspn (stirng, "\n") 返回到'\n' 字符的字符数,给出字符串的长度,不包括'\n'。 (非常方便的函数)它像strpbrk() 一样工作,但strpbrk() 不是计数,而是返回一个指向reject 中第一个字符位置的指针。
【解决方案2】:

在循环中终止你的字符串不是空值。您在循环之前使用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] = '\0';

        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;
}

【讨论】:

  • 谢谢!我不得不研究 for(;;) 是什么,但在我这样做之后,将我的循环更改为与你的更相似,现在我的程序可以工作了!
猜你喜欢
  • 1970-01-01
  • 2014-01-10
  • 1970-01-01
  • 2023-02-20
  • 2011-10-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-11
相关资源
最近更新 更多