PioneerRaptor
PioneerRaptor

Reputation: 69

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".

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. Here is the question I have to answer:

enter image description here

And here's my current output: enter image description here

Upvotes: 0

Views: 5282

Answers (2)

David C. Rankin
David C. Rankin

Reputation: 84579

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.

(note:, you can simply test len == 1 & *userText == 'd' to handle the exit on 'd' case without calling strcmp() -- up to you)

Before looking at a solution, you should avoid using Magic-Numbers in your code (e.g. 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.

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. You are using fgets() here (good for you!), 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. So, you simply need to ensure that the return is not NULL to validate characters were save in userText, e.g.

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

You invoke Undefined Behavior on your first iteration with len-1 in:

    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. Specifically:

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 "The value of an object with automatic storage duration is used while it is indeterminate (6.2.4, 6.7.9, 6.8)."

fgets() include the '\n' in the buffer filled

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". A simple, and robust way to remove the '\n' is by using strcspn(), e.g.

        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(), e.g.

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

Now your strcmp() checks will match.

To output the user-input in reverse, simply loop len times outputting characters starting from the last to the first. A while loop provides a simple manner of iterating, e.g.

        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)

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:

#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)

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.

Upvotes: 2

hgs3
hgs3

Reputation: 555

You're not null terminating your string in the loop. You've done it correctly before the loop, with 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.

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

Upvotes: 1

Related Questions