0

i want to read every line from a file which looks something like this:

readEveryLine
{       
  "Bart [m]" -> "Marge [f]";  
  "Lisa [f]" -> "Homer [m]"; 
  ...      
}

i want to use:

  1. fgets() to read the file line by line
  2. strncmp() to compare every line with a given string or see that it has just the right format

what i have:

while(fgets(*file_string, MAX_INPUT_STDIN, file) != NULL)
{       
  changeLastC(*file_string);  // function to change \n into \0 

    if (strncmp(*file_string, "readEveryLine\0", 14) == 0)
    {
      if (strncmp(*file_string, "{\0", 2) == 0)
      {
        // check the first -> relation
      }
    }
    else
    {
      printf("Error Parsing\n");
    } 
}

so the problem is that it just gives me an Error Parsing and i don`t know what i did wrong here.

Thanks a lot for helping me!

here i made a few things now (parsing the first two lines works now) : maybe anyone has got a good tip for me what i could do better. Thanks a lot.

if ((fp = fopen("df.dot","r")) == NULL)
{
  printf("Error: File Open\n");
  return 1;
}


int row = 0; // check row 1

while (fgets(buffer, MAX_PARSING, fp))
{        
  if ((row == 0) && strncmp(buffer, "readEveryLine\n", 14) == 0)
  {
    printf("%s", buffer);
  }
  else
  {
    printf("Parsing Error 1\n");
  }
}


int row1 = 1; // check row 2

while (fgets(buffer, MAX_PARSING, fp))
{     
  if ((row1 == 1) && strncmp(buffer, "{\n", 2) == 0)
  {
    printf("%s", buffer);
  }
  else
  {
    printf("Parsing Error 2\n");
  }
}


int row2 = 2; // check other rows (dynamic, could be even more or less)

while (fgets(buffer, MAX_PARSING, fp))
{ 
  if ((row2 == 2) && strncmp(buffer, "  ", 2) == 0)
  {
    const char *p1 = strstr(fp, "\"")+1;
    const char *p2 = strstr(p1, " [m]\"");
    const char *p3 = strstr(p1, " [f]\"");

    // extract male persons
    if (p1 && p2)
    {
      size_t len1 = p2 - p1;
      char* res1 = (char*)malloc(sizeof(char)*(len1 + 1));
      strncpy(res1, p1, len1);

      res1[len1] = '\0';

      // give res1 for functionMale() to work on that string
    }

    // extract female persons
    else if (p1 && p3)
    {
      size_t len2 = p3 - p1;
      char* res2 = (char*)malloc(sizeof(char)*(len2 + 1));
      strncpy(res2, p1, len2);

      res2[len2] = '\0';

      // give res2 for functionFemale() to work on that string
    }

    else if (strcmp(buffer, " -> ") == 0)
    {
      // work in progress (quite complicated to do this i think)
      // it has to be a realtion between two people
    }

    else if (strcmp(buffer, ";") == 0)
    {
      // work in progress
      // this sign can either exist like this:
      // "Bart [m]" -> "Marge [f]";

      // or like this:
      // "Marge [f]";
    }

    break;
  }
  else
  {
    printf("Parsing Error 3\n");
  }

  row2++;

}

// and the very last sign has to be }\n

MBD
  • 95
  • 3
  • 10
  • 1) Post definition of `string` and `changeLastC()`, 2) Insure compiler warnings are fully enabled. 3) Why expect `"readEveryLine\n"` to equal `"readEveryLine"`? Else the problem is not well specified. – chux - Reinstate Monica Jan 02 '17 at 18:39
  • 2
    Why on earth do you want to use `strncmp`? The correct function for this purpose is `strcmp`. – rici Jan 02 '17 at 20:42
  • @chux, There is no '\n' at the end of the line. According to the comment the `\n` is changed to `\0` by function `changeLastC()` – Gerhardh Jan 02 '17 at 23:57
  • @Gerhardh Yes my 3rd part of my comment _should_ not apply yet comments do not drive code - only hint at what is desired. OP's problem can be well caused by a `changeLastC()` that does not work, by wrong set up of `string` or by many other things. Posted code is insufficient to diagnose. – chux - Reinstate Monica Jan 03 '17 at 02:23
  • @chux, why is it insufficient? As I mentioned in my anser the loop is broken. You can never get anything than "Error Parsing". For the first line the first `if` is true but the second `if` is false because any string cannot match two different literals. For every other line of the file already the first `if` is false and we enter the else part where "Error Parsing" is printed. This is ame as what he describes as error. Maybe there are other hidden errors as well which he didn't mention yet. But he first needs to sort out this problem. – Gerhardh Jan 03 '17 at 07:43
  • @Gerhardh Note: "... any string cannot match two different literals. ..."` --> `strncmp(*file_string, "readEveryLine\0", 14)` can return match (a '0') and using a different string literal `strncmp(*file_string, "readEveryLine", 14)` can also return the same. But that is not the main issue here. – chux - Reinstate Monica Jan 03 '17 at 15:01
  • @chux. You are right, If one literal is substring of the other it is possible. But the literals involved in the problem are not constructed this way. – Gerhardh Jan 03 '17 at 15:07

1 Answers1

0

Your algorithm is already broken. You use the very same content of *file_string to compare it against tweo different strings. If you find a match for "readEveryLine" you need to read the next line from your file before you can get the next match for strncmp(). Otherwise the line from the file must match both "readEveryLine" and "{" to pass your second if condition, which is impossible.

Edit: Now as you have done some improvements I still think it will not work with your approach. The loops will not exit when they should and your if-else-cascade also does not seem to be a good idea. In your approach you will get messed up with reading too many lines while only 1 line should be parsed.

Maybe you should read a bit about state machines.

Here is a quick approach how I would address the problem:

enum { STATE_HEADER1, STATE_HEADER2, STATE_BODY, STATE_END} state;
int done = 0;
state = STATE_HEADER1;

while (fgets(buffer, MAX_PARSING, fp) && !done) {        
  if (state == STATE_HEADER1) {
    if (strcmp(buffer, "readEveryLine\n") == 0) {
      printf("%s", buffer);
      state = STATE_HEADER2;
    }
    else {
      printf("Parsing Error 1\n");
      done = 1;
    }        
  }
  else if (state == STATE_HEADER2) {
    if (strcmp(buffer, "{\n") == 0) {
      printf("%s", buffer);
      state = STATE_BODY;
    }
    else {
      printf("Parsing Error 2\n");
      done = 1;
    }
  }
  else if (state == STATE_BODY) {
    if (strcmp(buffer, "  ") == 0) {
      const char *p1 = strstr(buffer, "\"");
      const char *pm = strstr(p1, " [m]\"");
      const char *pf = strstr(p1, " [f]\"");
            char *res;
      const char *ptemp;
      int is_male;

      if (p1 && pf)  {
        p1 ++;
        is_male = 0;
        size_t len1 = pf - p1;
        res = malloc(len1 + 1);
        strcpy(res, p1);
        ptemp = pf+3; // point after closing \"

        // give res for functionFemale() to work on that string
      }
      else if (p1 && pm) {
        p1 ++;
        is_male = 1;
        size_t len1 = pm - p1;
        res = malloc(len1 + 1);
        strcpy(res, p1);
        ptemp = pm+3; // point after closing \"

        // give res for functionMale() to work on that string
      }
      else {
        done = 1;
        printf("Parsing Error 2\n");
      }

      // Now we have res and is_male holding name and gender.

      if (!done)
      {
        if (strncmp(ptemp, " -> ", 4) == 0) {

  // Handle this variant:
  // this sign can either exist like this:
  // "Bart [m]" -> "Marge [f]";

          // Do similar stuff as above for first name

          // Get second name + gender
          // Also check trailing ';' here
        }
        else if (strcmp(temp, ";\n") == 0) {

 // Handle this variant:
 // or like this:
 // "Marge [f]";

        }

      } // found "  "
      else {
        if (strcmp(buffer, "}\n") == 0) {
          state = STATE_END;
          done = 1;
          printf("That's it folks...\n"); 
        }
        else {
          done = 1;
          printf("Parsing Error 3\n");
        }
      }
    }
  } // STATE_BODY
} // while (fgets)

if (state == STATE_END) }
  // Success. :)
} else {
  // Something didn't match.
}

// close file, cleanup, etc.

I did not compile it yet but you should get the idea.

Gerhardh
  • 11,688
  • 4
  • 17
  • 39
  • sounds clear. the problem is, that i do not know how to parse the next line, make another if condition, then parse the third line, make the next if condition and so on. Thanks a lot for your help. – MBD Jan 02 '17 at 23:23
  • For example if i use a while loop like this: while(fgets(file_string, 19, file) ) and use this buffer for the first line then i am not sure if i should use a loop for each line and a new buffer. i think that i`m wrong with this. Thanks a lot for your help. – MBD Jan 02 '17 at 23:41
  • Try it with some pseudo code. Try to explain it to your rubber duck or your pet, what you want to do. You may also try to do it on paper. If there are phrases like "first ..., then..." you need a sequence of separate actions. No loop. If there are phrases like "For every line...." then you need a loop. With this it should be clear that you do not need a loop for the first 2 lines of your file. But you need a loop for any following line. And some handlnig for the last line ("}\n"). I would suggest that you dig into your C text book again and digest a bit more _when_ and _how_ loops are used. – Gerhardh Jan 03 '17 at 00:06
  • could you please give me a tip parsing the ; at the end of each line and parsing the very last two signs }\n at the end of the string? This would help me a lot. Thanks. – MBD Jan 05 '17 at 00:00
  • I've updated my answer. Your approach still reads multiple lines while the same line should be parsed. You might need a bit debugging for final details. – Gerhardh Jan 05 '17 at 08:23