strcmp on a line read with fgets
Asked Answered
A

6

10

I'm trying to compare two strings. One stored in a file, the other retrieved from the user (stdin).

Here is a sample program:

int main()
{
    char targetName[50];
    fgets(targetName,50,stdin);

    char aName[] = "bob";
    printf("%d",strcmp(aName,targetName));

    return 0;
}

In this program, strcmp returns a value of -1 when the input is "bob". Why is this? I thought they should be equal. How can I get it so that they are?

Apothem answered 8/3, 2010 at 21:13 Comment(4)
You should avoid using strcmp. Use strncmp instead, especially if you are comparing against a fixed-width string. Using strncmp(aName,targetName,strlen(aName)) should work for you here.Waist
bta's comment is just wrong -- use strncmp if you want to check against a prefix, rather than the entire string, so that eg. "bobcat" will also match.Dessau
I said that because strcmp will continue comparing until a NULL-terminator is reached or the strings differ. If you cannot be certain that your strings will always be properly NULL-terminated, strcmp can introduce buffer overflows and memory access violations. strncmp isn't just for reading prefixes; set the final parameter to the size of your fixed-length buffer to ensure that you aren't overflowing your array bounds.Waist
bta- your original comment says to use strlen to compute the size argument to strncmp. Doing so adds no additional protection that strcmp does not already have. Using a fixed size value, such as the buffer size as your second comment indicates, is when strncmp should be used.Prodrome
W
14

strcmp is one of the few functions that has the reverse results of true and false...if the strings are equal, the result is 0, not 1 as you would think....

if (strcmp(a, b)) {
    /* Do something here as the strings are not equal */
} else {
    /* Strings are equal */
}

Speaking of fgets, there is a likelihood that there is a newline attached to the end of the string...you need to get rid of it...

+-+-+-+--+--+
|b|o|b|\n|\0|
+-+-+-+--+--+

To get rid of the newline do this. CAVEATS: Do not use "strlen(aName) - 1", because a line returned by fgets may start with the NUL character - thus the index into the buffer becomes -1:

aName[strcspn(aName, "\n")] = '\0';

+-+-+-+--+
|b|o|b|\0|
+-+-+-+--+

Now, strcmp should return 0...

Wowser answered 8/3, 2010 at 21:21 Comment(5)
Thanks, other answers were good, but yours included a solution (I thought the problem might be \n, but thought it could have also been something else), because you include a solution (and a nice array picture lol), I've marked yours as the best. And yes, now strcmp works.Apothem
strcmp does not reverse the results of true and false. strcmp doesn't return a boolean, it returns an integral value based on how the two strings relate to each other. "if (strcmp(a,b))" works, but obscures this fact since when dealing with integral values, the "if" statement will treat it as "if (n != 0)". A more clear way of writing this would be "if (strcmp(a,b) != 0)" which state explicitly what is being tested.Latrena
@Torlack: or if (!strcmp(a,b))....regardless of what you say, anything that is non-zero is true, and zero is false hence my emphasis on it...Wowser
yea, strcmp actually returns 0 if they match, otherwise it will be some negative number if one is less than the other, or some positive, if one is greater then the other (it does some weird math, haha) But it is inverted in a way, 0 being 'true' and 1 or any other number = false. If you just think of the return value as 'the amount of difference' it makes more sense.Apothem
But your statement is still incorrect regarding it being the reverse. If what you said is true, then "if (strcmp(a,b)!=true)" would also be correct, but it is not (assuming that we are talking about a version of C or C++ with a proper implementation of a boolean that can only have the state of true and false). blogs.msdn.com/ericlippert/archive/2004/07/15/184431.aspxLatrena
S
7

fgets reads until it sees a newline then returns, so when you type bob, in the console, targetName contains "bob\n" which doesn't match "bob". From the fgets documenation: (bolding added)

Reads characters from stream and stores them as a C string into str until (num-1) characters have been read or either a newline or a the End-of-File is reached, whichever comes first. A newline character makes fgets stop reading, but it is considered a valid character and therefore it is included in the string copied to str. A null character is automatically appended in str after the characters read to signal the end of the C string.

You need to remove the newline from the end of targetName before you compare.

int cch = strlen(targetName);
if (cch > 1 && targetName[cch-1] == '\n')
   targetName[cch-1] = '\0';

or add the newline to your test string.

char targetName[50];
fgets(targetName,50,stdin);

char aName[] = "bob\n";
printf("%d",strcmp(aName,targetName));
Scandium answered 8/3, 2010 at 21:20 Comment(1)
I'd suggest using sizeof(targetName), don't hardcode it in the fgets().Had
C
3

The fgets is appending a \n to the string that you are pulling in from the user when they hit Enter. You can get around this by using strcspn or just adding \n onto the end of your string you're trying to compare.

printf("Please enter put FILE_NAME (foo1, 2, or 3), ls, or exit: \n");
fgets(temp, 8, stdin);
temp[strcspn(temp, "\n")] = '\0';
if(strcmp(temp, "ls") == 0 || strcmp(temp, "exit") == 0)

This just replaces the \n with a \0, but if you want to be lazy you can just do this:

printf("Please enter put FILE_NAME (foo1, 2, or 3), ls, or exit: \n");
fgets(temp, 8, stdin);
if(strcmp(temp, "ls\n") == 0 || strcmp(temp, "exit\n") == 0)

But it's not as elegant.

Curie answered 30/8, 2012 at 15:54 Comment(0)
F
2

Because fgets is embededing the newline character into the variable targetName. This is throwing off the comparison.

Foley answered 8/3, 2010 at 21:17 Comment(0)
G
1

fgets appends the newline to the string, so you'll end up with bob\n\0 which isn't the same as bob\0.

Gilford answered 8/3, 2010 at 21:18 Comment(0)
F
1

Mostly because of the end of line char in the input "\n" under unix like system.

Furmenty answered 8/3, 2010 at 21:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.