-3

I'm trying to recover a .txt file text to turn it into a record to create a basic "gps". I did manage to turn the text into a record, however, the longer the code run, the more frequents errors appear (like for exemple it write "hungrygry" instead of "hungry").

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

typedef int bool;
#define TRUE 1
#define FALSE 0

// definir string
typedef char string[1024];

//Define event
typedef struct{
  string action[49],preconds[24],add[24],delete[24];
  int NbPreconds,NbAdd,NbDelete;
}Event;

int parseLine(char source[], string cible[]){
  int i=0, n=0; // i: source[], n: cible[]
  while(source[i]!=':') i++; // go until ':'
    i++; // avancer au debut de la premiere chaine
    int j=i;  // point beginning of first chain with j
  while(source[i]!='\n'){
    if(source[i]==','){
      memcpy(&cible[n], &source[j], i-j); // extract characters from j to i
      n++;
      j=i+1;   // j beginning of chain
    }
    i++;
  }
  return n;
}

int main(){
  Event event[49];
  string start[49],finish[49];
  int NbStart,NbFinish,i,k,x,Z;
  char source[100];
  FILE* txtfile = fopen("school.txt","r"); // insérer "monkeys.txt" / "blocs.txt" / "school.txt" pour choisir le fichier
  if(txtfile == NULL){ // tester le fichier s'il existe
    printf("Fichier inexistant.\n");
    exit(0);
  }
  else{
    printf("Fichier ouvert.\n");
  }
  //Lire les Conditions de départ & de Fin
  fgets(source,100,txtfile);
  NbStart=parseLine(source,start);
  printf("Condition Départ:\n");
  for(i=0; i<NbStart;i++){
    printf("  - %s\n",start[i]);
  }
  fgets(source,100,txtfile);
  string tempo[]={""};
  NbFinish=parseLine(source,finish);
  printf("Condition Fin:\n");
  for(i=0;i<NbFinish;i++){
    printf("  - %s\n",finish[i]);
  }
  //read action
  int NbE=0;
  while(fgets(source,100,txtfile)!=NULL){
    fgets(source,100,txtfile);
    parseLine(source, event[NbE].action);

    //read preconds
    fgets(source,100,txtfile);
    event[NbE].NbPreconds=parseLine(source, event[NbE].preconds);
    for(i=0;i<event[NbE].NbPreconds;i++){
      printf("Precond: %s\n",event[NbE].preconds[i]);
    }

    //read add
    fgets(source,100,txtfile);
    event[NbE].NbAdd=parseLine(source, event[NbE].add);
    for(i=0;i<event[NbE].NbAdd;i++){
      printf("Add: %s\n",event[NbE].add[i]);
    }
    //read delete
    fgets(source,100,txtfile);
    event[NbE].NbDelete=parseLine(source, event[NbE].delete);
    for(i=0;i<event[NbE].NbDelete;i++){
      printf("Delete: %s\n",event[NbE].delete[i]);
    }
  }
  bool Test=0; //Not complete
  while(Test==0){
    Test=1;
    for(i=0;i<NbStart;i++){
      for(k=0;k<NbStart;k++){
        if(start[k]==finish[i]){
          Z++;
          if(Z==NbFinish){
            Test=1;
          }
        }
      }
    }
    Z=0;
  }
  if(Test==1){
    printf("Le résultat a était trouvé en %d étapes.\n",x);
  }
  fclose(txtfile);
}

Input:

start:at door,on floor,has ball,hungry,chair at door,
finish:not hungry,
****
action:climb on chair,
preconds:chair at middle room,at middle room,on floor,
add:at bananas,on chair,
delete:at middle room,on floor,
****
action:push chair from door to middle room,
preconds:chair at door,at door,
add:chair at middle room,at middle room,
delete:chair at door,at door,
****
action:walk from door to middle room,
preconds:at door,on floor,
add:at middle room,
delete:at door,
****
action:grasp bananas,
preconds:at bananas,empty handed,
add:has bananas,
delete:empty handed,
****
action:drop ball,
preconds:has ball,
add:empty handed,
delete:has ball,
****
action:eat bananas,
preconds:has bananas,
add:empty handed,not hungry,
delete:has bananas,hungry,

Output:

Fichier ouvert.  
Condition Départ:
  - at door      
  - on floor
  - has ball
  - hungry
  - chair at door
Condition Fin:
  - not hungry
Precond: chair at middle room
Precond: at middle room
Precond: on floor
Add: at bananas
Add: on chair
Delete: at middle room
Delete: on floor

Precond: chair at doorle room
Precond: at doorle room
Add: chair at middle room
Add: at middle room
Delete: chair at doorm
Delete: at doorr

Precond: at doort doorle room
Precond: on floore room
Add: at middle roome room
Delete: at doort doorm

Precond: at bananasoorle room
Precond: empty handedom
Add: has bananasoome room
Delete: empty handedrm

Precond: has ballasoorle room
Add: empty handedome room
Delete: has ballndedrm

Precond: has bananasorle room
Add: empty handedome room
Add: not hungryroom
Delete: has bananasdrm
Delete: hungryrr

Le résultat a était trouvé en 0 étapes.
Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
  • 1
    Did you debug the code to determine where it first goes wrong? – jarmod Apr 27 '23 at 00:54
  • Have you tried running your code line-by-line in a debugger while monitoring the control flow and the values of all variables, in order to determine in which line your program stops behaving as intended? If you did not try this, then you may want to read this: [What is a debugger and how can it help me diagnose problems?](https://stackoverflow.com/q/25385173/12149471) You may also want to read this: [How to debug small programs?](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/) – Andreas Wenzel Apr 27 '23 at 00:56
  • I debugged my code but there is no error, it's just that at the beginning, the record is correct, but as it proceed it gets more and more errors. If I had to do a "wild" guess it would be that some memory is kept for some reasons when it shouldn't. – Silver Winter Apr 27 '23 at 01:06
  • _"I debugged my code but there is no error"_ - that's not where debugging stops. Follow your variables running your program in a debugger. Step through the code, line by line and see what it does. Halt when it does something unexpected. – Ted Lyngmo Apr 27 '23 at 01:14
  • You are printing the value of `x`, but it has an indeterminate value, because you never set it to anything. – Andreas Wenzel Apr 27 '23 at 01:56
  • I added the input and the output, and the x value is for later to see after how many successful test the code ends (which can't be done for now since I can't do the test correctly since there is errors in the records). – Silver Winter Apr 27 '23 at 02:07
  • 1
    I don't see anything like a 0 terminator on any string (mind you I didn't look that hard...) – John3136 Apr 27 '23 at 02:17
  • 2
    You never terminate the strings. Where you have `memcpy(&cible[n], &source[j], i-j); // extract characters from j to i` you need to add `cible[n][i-j] = '\0';` just after that. – Jerry Jeremiah Apr 27 '23 at 02:32
  • You should explain what you are trying to do "recover a .txt file text to turn it into a record to create a basic "gps"" means absolutely nothing to a 3rd party. Output, I assume is from your program, what is the expected output? – Allan Wind Apr 27 '23 at 02:40
  • @AllanWind The output he gives is the expected output. The actual output is much different. He should have given both. When I run it I get: https://onlinegdb.com/lgaZy94CK – Jerry Jeremiah Apr 27 '23 at 02:47
  • Try emptying _source_ between reads - maybe it's not being cleared out and retains data from the previous read. – FreudianSlip Apr 27 '23 at 04:29
  • One more point, please consider using a more dynamic approach. You are using many static arrays and cascating them (`string` -> `Event` -> `Event event[49]`). Your `event` instance uses 5 MByte of stack memory. Depending on your compiler and compiler settings this may cause unexpected behaviour. How many stack space have you reserved in your compiler settings? – QuicheLorraine Apr 27 '23 at 06:55

1 Answers1

3

How to find the bug using the GNU debugger

Here is an example on how to find the bug using the GNU debugger (GDB). If you are using an IDE with another debugger, you can use that too. All debuggers should have the features described below, though the syntax may be different. Also, some debuggers allow you to issue commands by clicking on buttons with the mouse, instead of typing commands.

In your posted output, the first line which is incorrect is the following line:

Precond: chair at doorle room

It should be:

Precond: chair at door

This line of output is printed by the code line

printf("Precond: %s\n",event[NbE].preconds[i]);

and event[NbE].preconds[i] should have been set by the line

event[NbE].NbPreconds=parseLine(source, event[NbE].preconds);

which is two lines further up in your program. It is line number 69.

Therefore, we load your program into GDB and set a breakpoint on line 69, using the following GDB command:

b 69

We then start running the program using the

r

command.

The program will now run to the first time it reaches the breakpoint on line 69.

We now set GDB to display the value of event[NbE].preconds whenever the program stops, using the following command:

display event[NbE].preconds

GDB now tells me that this variable has the following value:

1: event[NbE].preconds = {'\000' <repeats 1023 times> <repeats 24 times>}

This means that all elements of all arrays in the 2D array have the value 0. It will probably tell you the same, although this is not guaranteded, because you did not initialize the values in any way.

We now go to the next line by stepping over the function call, using the following command:

n

GDB now tells us the following for event[NbE].preconds:

1: event[NbE].preconds = {"chair at middle room", '\000' <repeats 1003 times>, "at middle room", '\000' <repeats 1009 times>, "on floor", '\000' <repeats 1015 times>, '\000' <repeats 1023 times> <repeats 21 times>}

These strings that were created by parseLine look good, so we continue running the program until the breakpoint is reached again, by using the following command:

c

GDB tells us that event[NbE].preconds has not been changed since the last time the program stopped, so we step over the function call to parseLine again by using the following command:

n

GDB now tells us that event[NbE].preconds has changed to the following value:

1: event[NbE].preconds = {"chair at doorle room", '\000' <repeats 1003 times>, "at doorle room", '\000' <repeats 1009 times>, "on floor", '\000' <repeats 1015 times>, '\000' <repeats 1023 times> <repeats 21 times>}

This value is incorrect. So we now know that the second time line 69 is executed, the function parseLine will give us invalid results. Therefore, we restart the program using the command

r

and we tell GDB that we no longer want to be told the value of event[NbE].preconds every time the program stops, by using the following command:

undisplay 1

We now repeat the steps above except for the display command, but the second time we hit the breakpoint on line 69, we don't use the n command to step over the function call to parseLine, but rather the

s

command to step into the function call, so that we can see exactly how the function is generating the incorrect output.

After stepping into the function, we issue the command

display cible[0]

to monitor the content of that string.

After issuing the command

n

to jump to the next line a few times (you can simply press ENTER to repeat the last command), we see that the call to memcpy in that function will change the content of cible[0] from

chair at middle room

to:

chair at doorle room

We see that memcpy overwrote the first 13 characters of cible[0], but did not write a null terminating character. Therefore, the characters le room are remnants from middle room, which was part of the previous content of the string.

The rest of the function parseLine also never writes a terminating null character to the string, so the incorrect string eventually gets passed back and printed.

Therefore, we now know that the obvious fix is for the function parseLine to add a terminating null character to the string after the call to memcpy.

Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39