Reputation: 69
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:
Upvotes: 0
Views: 5282
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
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