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?

Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254
Blackbinary
  • 3,936
  • 18
  • 49
  • 62
  • 1
    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. – bta Mar 08 '10 at 21:33
  • 1
    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. – Chris Dodd Mar 08 '10 at 21:58
  • 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. – bta Mar 08 '10 at 22:17
  • 1
    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. – bboe Mar 13 '14 at 16:56

6 Answers6

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

Community
  • 1
  • 1
t0mm13b
  • 34,087
  • 8
  • 78
  • 110
  • 1
    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. – Blackbinary Mar 08 '10 at 21:27
  • 1
    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. – Torlack Mar 08 '10 at 21:35
  • @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... – t0mm13b Mar 08 '10 at 21:57
  • 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. – Blackbinary Mar 08 '10 at 22:25
  • 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). http://blogs.msdn.com/ericlippert/archive/2004/07/15/184431.aspx – Torlack Mar 09 '10 at 15:24
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));
John Knoeller
  • 33,512
  • 4
  • 61
  • 92
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.

Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254
2

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

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
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.

Blindy
  • 65,249
  • 10
  • 91
  • 131
1

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