Search code examples
carraysdynamic-arrays

How to read lines from file into an array of strings?


#define LINES 4
#define LENGHT 30

typedef struct
{
  char *name;
  char *phoneNumber;
  char *location;
  char *traveltype;
} Client;

int main(int argc, char *argv[])
{
  char *filename = argv[1];
  char clientData[LINES][LENGHT];
  readClientData(filename, clientData);

  /* How to do this client struct */
  Client *client = createClient(clientData[0], clientData[1], clientData[2], clientData[3]);
  return 0;
}

void readClientData(char *filename, char clientData[LINES][LENGHT])
{
  FILE *file = fopen(filename, "r");
  if (file == NULL)
  {
    perror("Error while opening file!");
    exit(1);
  }

  char line[LENGHT];
  int i = 0;
  while (fgets(line, LENGHT, file) != NULL)
    clientData[i] = line; /* How to do this */ 
    i++;
  fclose(file);
}

Coming from a pythonista world I'm a bit confused how things are working here. It is clear that I can't just add lines to the list, nor can acces the lines the way I do to initialize the client struct.

Maybe I should just use a char *clientData[], but don't know how.


Solution

  • To copy C strings, you need to use:

    char * strcpy ( char * destination, const char * source );

    from the Standard C String Library, string.h.

    However, notice, than unlike Python (your background), indentation does not define the body of the loop, you need to use curly brackets!

    So, you need to do this:

    while (fgets(line, LENGHT, file) != NULL)
    {
      strcpy(clientData[i], line);
      i++;
    }
    

    Without the curly brackets, only the first immediate line of code will be considered to be the body of the loop. So, in the example above, the body of the loop - if we did not use curly brackets - would be only the call to the method for copying strings, and the increment of the counter would not be part of the loop!


    You need to define, or just declare a method before using it, which you didn't do in your example.


    Since you only need one client, you do not need a pointer, you can simply create one, by doing Client client;.

    In order to keep it simple, use the knowledge you gathered from reading this answer so far, and do define the maximum length of the strings (that is the size of the arrays of characters every field of your struct gets to have). Remember, that C strings have to be NULL terminated, so, the maximum length of them is actually LENGTH - 1.

    If you keep the fields of the struct as pointers to char, then you'd need to dynamically allocate memory, or set the pointer point to corresponding string of the clientData array (similarly to what you did for the file name). I suggest you get some experience in C first, and then try implementing these two approaches.

    Now, you are ready to do:

    strcpy(client.name, clientData[0]);
    ...
    strcpy(client.traveltype, clientData[3]);
    

    Complete working example:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    #define LINES 4
    #define LENGTH 30
    
    typedef struct
    {
      char name[LENGTH];
      char phoneNumber[LENGTH];
      char location[LENGTH];
      char traveltype[LENGTH];
    } Client;
    
    void readClientData(char *filename, char clientData[LINES][LENGTH]);
    void printClient(Client client);
    
    int main(int argc, char *argv[])
    {
    
      char *filename = NULL;
      if(argc > 1)
        filename = argv[1];
      else
      {
        printf("Usage: %s <filename>\n", argv[0]);
      }
      char clientData[LINES][LENGTH];
      readClientData(filename, clientData);
    
      Client client;
      strcpy(client.name, clientData[0]);
      strcpy(client.phoneNumber, clientData[1]);
      strcpy(client.location, clientData[2]);
      strcpy(client.traveltype, clientData[3]);
    
      printClient(client);
    
      return 0;
    }
    
    void readClientData(char *filename, char clientData[LINES][LENGTH])
    {
      FILE *file = fopen(filename, "r");
      if (file == NULL)
      {
        perror("Error while opening file!");
        exit(1);
      }
    
      char line[LENGTH];
      int i = 0;
      while (fgets(line, LENGTH, file) != NULL)
      {
        line[strcspn(line, "\n")] = 0;
        strcpy(clientData[i], line);
        i++;
      }
      fclose(file);
    }
    
    void printClient(Client client)
    {
        printf("%s, %s, %s, %s\n", client.name, client.phoneNumber, client.location, client.traveltype);
    }
    

    Output:

    Georgioss-MBP:Desktop gsamaras$ cat test.txt 
    MegasAlexandros
    3335632320
    Greece
    Cosmos
    Georgioss-MBP:Desktop gsamaras$ gcc main.c
    Georgioss-MBP:Desktop gsamaras$ ./a.out test.txt 
    MegasAlexandros, 3335632320, Greece, Cosmos
    

    PS: I used this in my example: Removing trailing newline character from fgets() input