简体   繁体   English

ANSI C:您如何从用户那里获取输入然后反向打印?

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

I'm attempting to write a program (for class) that allows a user to type in a string and then outputs that string in reverse and it does this until the user types in "Done", "done", or "d".我正在尝试编写一个程序(用于类),该程序允许用户输入一个字符串,然后反向输出该字符串,直到用户输入“完成”、“完成”或“d”。

Here is my current code:这是我当前的代码:

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

As you can see, I used fgets because I have to allow the user to input a string that includes spaces and then I also cleared the buffer to avoid new lines.如您所见,我使用 fgets 是因为我必须允许用户输入一个包含空格的字符串,然后我还清除了缓冲区以避免换行。 Here is the question I have to answer:这是我必须回答的问题:

在此处输入图像描述

And here's my current output:这是我目前的 output: 在此处输入图像描述

You are making things a bit difficult on yourself.你让自己的事情变得有点困难。 Once you have the length, you can simply loop that many times, outputting characters from the end to the beginning and then outputting a newline.一旦你有了长度,你可以简单地循环多次,从结尾输出字符到开头,然后输出换行符。 Simply enclose that all in a loop to continually take input and ceck for your 'd' , "Done" or "done" to break the loop.只需将所有内容都包含在一个循环中,以不断获取输入并检查您'd'"Done""done"以打破循环。

( note: , you can simply test len == 1 & *userText == 'd' to handle the exit on 'd' case without calling strcmp() -- up to you) 注意: ,您可以简单地测试len == 1 & *userText == 'd'来处理'd'情况下的退出,而无需调用strcmp() - 由您决定)

Before looking at a solution, you should avoid using Magic-Numbers in your code (eg 50 ).在查看解决方案之前,您应该避免在代码中使用幻数(例如50 )。 Instead:反而:

...
#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;

That way if you need to change the length of your string, you have a single, convenient location to adjust the length without having to pick though all function calls or loop limits to make the change.这样,如果您需要更改字符串的长度,您就有一个方便的位置来调整长度,而无需选择所有 function 调用或循环限制来进行更改。

Validate EVERY Input验证每个输入

Regardless what input function you are using, you cannot use it correctly unless you are checking the return to determine if the input succeeded or failed.无论您使用的是什么输入 function ,除非您检查返回以确定输入是成功还是失败,否则您无法正确使用它。 You are using fgets() here (good for you,).您在这里使用fgets() (对您有好处)。 but you still need to check the return.但是您仍然需要检查退货。 fgets() will return a pointer to the filled buffer on success, or NULL on EOF or stream error. fgets()将在成功时返回指向已填充缓冲区的指针,或在EOFNULL错误时返回 NULL。 So, you simply need to ensure that the return is not NULL to validate characters were save in userText , eg因此,您只需确保返回不是NULL即可验证字符是否保存在userText中,例如

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

You invoke Undefined Behavior on your first iteration with len-1 in:您使用len-1在第一次迭代中调用未定义行为

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

On the first iteration, len is uninitialized, and any attempt to use the value of a variable with automatic storage duration while its value is indeterminate results in undefined behavior.在第一次迭代中, len未初始化,并且任何尝试使用具有自动存储持续时间的变量的值而其值不确定会导致未定义的行为。 Specifically:具体来说:

C11 Standard - 6.7.9 Initialization(p10) "If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate." C11 标准 - 6.7.9 初始化(p10) “如果具有自动存储持续时间的 object 未显式初始化,则其值是不确定的。” and C11 Standard - J.2 Undefined Behavior "The value of an object with automatic storage duration is used while it is indeterminate (6.2.4, 6.7.9, 6.8)."C11 标准 - J.2 未定义行为“在不确定时使用具有自动存储持续时间的 object 的值(6.2.4、6.7.9、6.8)。”

fgets() include the '\n' in the buffer filled fgets() 在填充的缓冲区中包含'\n'

When you attempt your strcmp() of "d" , "Done" , and "done" , you will never match "d" , "Done" , and "done" because what is actually contained in the buffer is "d\n" , "Done\n" , and "done\n" .当您尝试strcmp()"d""Done""done"时,您将永远不会匹配"d""Done""done" ,因为缓冲区中实际包含的是"d\n""Done\n""done\n" A simple, and robust way to remove the '\n' is by using strcspn() , eg删除'\n'一种简单而可靠的方法是使用strcspn() ,例如

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

You can obtain the length of the line without the '\n' by simply saving the return of strcspn() , eg您可以通过简单地保存strcspn()的返回来获得不带'\n'的行的长度,例如

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

Now your strcmp() checks will match.现在您的strcmp()检查将匹配。

To output the user-input in reverse, simply loop len times outputting characters starting from the last to the first.对于 output 用户输入反向,只需循环len次从最后一个到第一个输出字符。 A while loop provides a simple manner of iterating, eg while 循环提供了一种简单的迭代方式,例如

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

( note: there is no need to printf ("%c", ... a single character, that is what putchar() or fputc() is for. A good compiler will normally make that optimization for you, but it is good to show an understanding of how output is taking place) 注意:不需要printf ("%c", ...单个字符,这就是putchar()fputc()的用途。一个好的编译器通常会为您进行优化,但这很好了解 output 是如何发生的)

Putting it altogether and providing the optional "user str: " prompt for user input, and the optional "reversed: " prefix for the output (better than leaving your user looking at a blinking cursor wondering if your program has hung), you could do:总而言之,为用户输入提供可选的"user str: "提示,并为 output 提供可选的"reversed: "前缀(比让您的用户看着闪烁的 cursor 想知道您的程序是否已挂起要好),您可以这样做:

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

( note: return 0; is the default with C99 forward, but since you specify C89 it is necessary) 注意: return 0;是 C99 转发的默认值,但由于您指定 C89,所以它是必要的)

Example Use/Output示例使用/输出

$ ./bin/outputrev

user str: Hello there
reversed: ereht olleH

user str: Hey
reversed: yeH

user str: done

Or:或者:

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

Look things over and let me know if you have any further questions.如果您有任何其他问题,请仔细查看并告诉我。

You're not null terminating your string in the loop.您不是 null 在循环中终止您的字符串。 You've done it correctly before the loop, with len = strlen(userText);您在循环之前已经正确完成了len = strlen(userText); but you're not doing it in the loop itself.但是您不是在循环本身中执行此操作。 You need to add the null terminator again after you've read from stdin.从标准输入读取后,您需要再次添加 null 终止符。

A better solution to this problem is to use a mid-test loop rather than a pre-test loop.解决此问题的更好方法是使用中间测试循环而不是预测试循环。 That way, you can read from stdin in one place in the code rather than two, eliminating code duplication.这样,您可以在代码中的处而不是两处读取标准输入,从而消除代码重复。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM