3

I have a delete function on array of structures books. I'm passing it an array of records, author of book and name of book and size of the list.

Now here given that list[0].author and list[5].author and author all are equal to "Dan Brown" (same string)

void delete(struct books *list,char author[],char name[],int n)
{
    int i,a;
    a=strcmp(list[0].author,list[5].author);
    printf("%d\n",a);              // prints 0
    a=strcmp(list[0].author,author);
    printf("%d\n",a);              // prints other than 0
}    

Why is it happening? What's wrong here?

  • 5
    Are you sure there isn't a `'\n'` at the end of some of the strings? – Giulio Franco Oct 26 '13 at 16:59
  • `list[0].author` gets input from `fgets()` function and `author` from `strcpy()` –  Oct 26 '13 at 17:03
  • 1
    Either use a debugger or `printf` all the 3 strings `printf("%s|%s|%s|\n", list[0].author, list[5].author, author);` should print `Dan Brown|Dan Brown|Dan Brown|` – Giulio Franco Oct 26 '13 at 17:03
  • Use `strsrt()` to find substring, or check `strlen()` to see if strings are really the same... Check more than one thing sometimes is necessary to validate input before processing it. – ryyker Oct 26 '13 at 17:04
  • @GiulioFranco it's not printing `Dan Brown|Dan Brown|Dan Brown|` but printing `Dan Brown` in 3 different lines. I think `fgets()` leaves a stray `\n` at the end(?) –  Oct 26 '13 at 17:07
  • 1
    @xmpirate Check the length of each string using `strlen` – digital_revenant Oct 26 '13 at 17:07
  • @ryyker, Do you mean `strchr()` instead od `strstr()`? (`\n` is a `char`) – David Ranieri Oct 26 '13 at 17:10
  • @Kunal yeah the lengths are different. –  Oct 26 '13 at 17:12
  • @xmpirate This means you have stray characters. Prune them. – digital_revenant Oct 26 '13 at 17:15
  • @xmpirate I updated my answer with a couple of ways to get around the `'\n'` issue – Giulio Franco Oct 26 '13 at 17:23
  • 1
    @AlterMann - no, `strstr()`. I am suggesting that using two methods to verify the input of each string (i.e. `strlen()` & `strstr()`) would be one way to address the issue OP is having. Actually, when dealing with strings, assumptions are often dangeous, and double checking is always a good idea. – ryyker Oct 26 '13 at 17:30

3 Answers3

2

From the documentation of fgets:

Reading stops when a newline character is found, at end-of-file or error. The newline, if any, is retained.

This means that fgets will not remove the final '\n' from the end of the read string. Thus, your strings are:

  1. "Dan Brown"
  2. "Dan Brown"
  3. "Dan Brown\n"

They're not equal.

This is a very common issue when using fgets. That's why I usually prefer scanf, like this:

char buffer[BUF_LEN];
char format[16];
int scanf_result;

sprintf(format, "%%%u[^\n]", BUF_LEN);
//....
do
{
  //TODO: Ask for input
  scanf_result = scanf(format, buffer);
  switch (scanf_result)
  {
    case -1: //TODO: Print error message and exit
    case 0: //TODO: Print error mesage and break
  }
  //Discard remainings of buffered input line
  while (getchar() != '\n') {;}
} while (1); //Ugly, but plain

Otherwise, you can use fgets with something like this:

int buf_len;

//TODO: Ask for input
while (fgets(buffer, BUF_LEN, stdin) == NULL)
{
  //TODO: Check and handle error
}
buf_len = strlen(buffer);
//Remove trailing '\n', if present
if (buffer[buf_len - 1] == '\n')
{
  buffer[--buf_len] = '\0';
}

Even though it's easier, I don't like this second method, because strlen scans the string another time to determine its length. In most cases, this is not a performance issue, I avoid it because I have my own mental issues.

Giulio Franco
  • 3,170
  • 15
  • 18
  • How can I remove the stray `\n`? –  Oct 26 '13 at 17:08
  • 1
    @xmpirate `str[strlen(str)-1] = '\0'` – digital_revenant Oct 26 '13 at 17:10
  • 1
    @Giulio Franco: +1, according to documentation as you have mentioned,the newline, if any, is retained. Do `buffer[--buf_len] = '\0';` without a check if this one is really a `\n` isn't dangerous? I mean, it may not be the new line. I've always check it – The Mask Oct 26 '13 at 17:37
  • @TheMask yes, you're right. It's dangerous. As I wrote, I never really use that. – Giulio Franco Oct 26 '13 at 17:55
  • strlen() can return zero: `if (buffer[buf_len - 1] == '\n')` -->>`if (buff_len && buffer[buf_len - 1] == '\n')` Less imporatant: buff_len should be a size_t, not an int. – wildplasser Oct 26 '13 at 18:03
  • @wildplasser strlen **won't** return zero, because I know the buffer is large enough to hold 1 char and the trailing `'\0'`, and `fgets` gets a line. So, if `fgets` succeeds, then buffer will contain at least "\n", and so buf_len will be at least 1. As for the `size_t`, when I faced these issue against C, i barely knew `int` and `char`. I tried to keep it easy, since 4 instructions to read a line may be traumatic. – Giulio Franco Oct 27 '13 at 00:10
  • @GiulioFranco: Try reading a binary file, with NUL characters in it (for testing purposes you can use /dev/zero) fgets will stop at the first NUL byte, and strlen() will report zero length if that byte happens to be the first in the line. And the buffer size is irrelevant here. BTW what is `while (fgets(buffer, BUF_LEN, stdin) == NULL)` supposed to do , wait untill the EOF disappears? – wildplasser Oct 27 '13 at 09:43
  • @wildplasser From `man fgets` "fgets() and gets() functions do not distinguish between end-of-file and error, and callers must use feof(3) and ferror(3) to determine which occurred.". That's exactly what you're supposed to do in that while. Determine what happened, if it's something recoverable, go to the next iteration, otherwise return an error. – Giulio Franco Oct 27 '13 at 14:25
  • And fgets won't work properly if the file contains nul bytes (scanf won't either), regardless of what you do, because there is no way to know how much fgets actually read. – Giulio Franco Oct 27 '13 at 14:31
1

You should verify your inputs. Sometimes by more than one method is necessary. Here, I am using strlen(), and strstr(), because if the length is ==, and a substring exists, then the strings ARE equal. So, try something like this to verify the input strings are what you thing they are before making a conclusion:

Note: the enum is of course not necessary, but included here to add clarity to example of output.

enum    {
    SAME,     //0
    NOT_SAME  //1
}

void delete(struct books *list,char author[],char name[],int n)
{
    int i,a, len1, len2;
    A = NOT_SAME;
    len1 = strlen(list[0].author);
    len2 = (list[5].author);
    if(strstr(list[0].author,list[5].author) && (len1==len2)) a = SAME;
    printf("%d\n",a);              


    a = NOT_SAME;
    len1 = strlen(list[0].author);
    len2 = (author);
    if(strstr(list[0].author,author) && (len1==len2)) a = SAME;
    printf("%d\n",a);              

}    
ryyker
  • 22,849
  • 3
  • 43
  • 87
0

check second strings by printing character by character.

Especially author string.

for(i=0; i < strlen(list[0].author);i++)
{
   if(list[0].author[i]!=author[i])
   {
     printf("this is position is not matching\n",i+1);
     //try to print characters and also print ascii characters.
     break; 
   }

}
//or simply try to use strncpy() 
Gangadhar
  • 10,248
  • 3
  • 31
  • 50