简体   繁体   中英

Reading info from a txt file into an array in c

Here is the array and structs used

typedef struct
{
    int day;
    int month;
    int year;
} dob;
typedef struct
{
    int id;
    char first_name[20];
    char last_name[20];
    float salary;
    dob birthdate;
    char address[100];
    char phone_number[20];
    char email[30];
} employee;
int counter = 0;
employee e[100];

I want to read the info and input into an array the info in the files is like

1,Steven1,Thomas,2001,10-06-1995,1 Elhoreya Street,01234567891,1sthomas@gmail.com
2,Steven2,Thomas,2002,10-06-1995,2 Elhoreya Street,01234567892,2sthomas@gmail.com
3,Steven3,Thomas,2003,10-06-1995,3 Elhoreya Street,01234567893,3sthomas@gmail.com
4,Steven4,Thomas,2004,10-06-1995,4 Elhoreya Street,01234567894,4sthomas@gmail.com
5,Steven5,Thomas,2005,10-06-1995,5 Elhoreya Street,01234567895,5sthomas@gmail.com

I can't seem to get a code to input this info

If your input file is as shown, then a solid approach is to read each line of the input file into a sufficiently sized buffer (character array) with fgets() (as you do with all input) and then parse the needed information from the buffer using sscanf() . This approach will server you well whether you are reading records from a file or taking user-input from the command line. In this case it also avoids having to match input with the correct struct member as you would need to do if looping with strtok() .

(With the limited number of fields you could just write out each call to strtok() and handle the strings one-at-a-time to match with the correct struct member. You could do the same parsing the buffer with a pair of pointers or alternating calls to strcspn() and strspn() )

Before looking at the way to read each line and split with sscanf() , let's avoid using MagicNumbers to size the arrays in your code and instead declare constants to use for that purpose. That way if you need to resize, you have one convenient place to make the change instead of having to pick though each declaration and loop limit in your code. You can define one or more constants with the #define statement or by using a global enum , eg

/* if you need constants, #define them, or use a global enum */
enum { C20 = 20, C30 = 30, C100 = 100, MAXC = 1024 };

(choose meaningful names, here 'C' was appended to provide an easy way to see where the constants were substituted in the array declarations while still appearing close to your original)

When reading from a file, you should be able to determine the max line lengths. For general use a 1K buffer (1024-bytes) or a 2K buffer will do generally. If you are writing code for an embedded system where memory is limited, reduce the general size as needed. Here a simple 1K buffer is used:

int main (int argc, char **argv) {
  
  char buf[MAXC];       /* buffer to hold each line read from file */
  int n = 0;
  employee e[C100];
  ...

Now start your read-loop ensuring you only loop while available space remains in your e[] array, eg

  /* while array not full and line read from file */
  while (n < C100 && fgets (buf, MAXC, fp)) {
    ...

With buf holding one complete line from your file, now call sscanf() to parse the needed values from the buffer. NOTE: "day-mo-year" is read as a single string and then later split into integer values for birthdate to simply the sscanf() format-string:

    char dobtmp[C30] = "";  /* temp buffer to hold "day-mo-year" */
    
    /* parse needed info fron buf, with field-width to protect array bounds
     * VALIDATE return -- all conversions successful
     */
    if (sscanf (buf,  "%d, %19[^,], %19[^,],%f, %29[^,],"
                      " %99[^,], %19[^,], %29[^\n]",
                      &e[n].id, e[n].first_name, e[n].last_name, &e[n].salary,
                      dobtmp, e[n].address, e[n].phone_number, 
                      e[n].email) == 8) {
    ...

( note: you must use the field-width modifier to protect the array bounds of .first_name , .last_name , etc.. anywhere you are reading into a character array to prevent buffer overrun)

Also, as you look at the format-string, make sure you understand the purpose of including a space before the %[...] conversion-specifier and what potential variation in your data file that protects against...

You must VALIDATE the return from sscanf() to ensure that each conversion you specify -- in fact took place. Above sscanf() is place in an if() statement and the code will only increment the employee count if ALL VALIDATIONS are successfully passed.

After the call to sscanf() above, dobtmp holds the "day-mo-year" string that needs to be further split to obtain the individual integer values for birthdate . A simple function can help there:

/* returns 1 if dob filled, 0 otherwise */
int splitdob (dob *d, const char *s)
{
  return sscanf (s, "%d-%d-%d", &d->day, &d->month, &d->year) == 3;
}

So all that remains is calling splitdob() and VALIDATE the return and if it passes, then, and only then, increment the employee counter (renamed n above for convenience). The complete read-loop and separate of values into your struct would then be:

  /* while array not full and line read from file */
  while (n < C100 && fgets (buf, MAXC, fp)) {
    char dobtmp[C30] = "";  /* temp buffer to hold "day-mo-year" */
    
    /* parse needed info fron buf, with field-width to protect array bounds
     * VALIDATE return -- all conversions successful
     */
    if (sscanf (buf,  "%d, %19[^,], %19[^,],%f, %29[^,],"
                      " %99[^,], %19[^,], %29[^\n]",
                      &e[n].id, e[n].first_name, e[n].last_name, &e[n].salary,
                      dobtmp, e[n].address, e[n].phone_number, 
                      e[n].email) == 8) {
      
      /* separate dobtmp into birthdate, store in employee */
      if (splitdob (&e[n].birthdate, dobtmp)) {
        n++;    /* update count only after ALL validations passed */
      }
    }
  }

With ALL VALIDATIONS passed before the employee counter is incremented, you ensure all data in each employee is valid. (you validate every step in your code that the continued operation of your program depends on and handle any error that arises)

A short, complete example program to process your sample date could look like:

#include <stdio.h>

/* if you need constants, #define them, or use a global enum */
enum { C20 = 20, C30 = 30, C100 = 100, MAXC = 1024 };

typedef struct {
  int day;
  int month;
  int year;
} dob;

typedef struct {
  int id;
  char first_name[C20];     /* size with constants, not MagicNumbers */
  char last_name[C20];
  float salary;
  dob birthdate;
  char address[C100];
  char phone_number[C20];
  char email[C30];
} employee;

/* returns 1 if dob filled, 0 otherwise */
int splitdob (dob *d, const char *s)
{
  return sscanf (s, "%d-%d-%d", &d->day, &d->month, &d->year) == 3;
}

/* function to output single employee data */
void prnemployee (employee *e)
{
  char nametmp[2 * C20],    /* temp buffers for combined name and dob */
       dobtmp[C20];
  
  /* join first last names and birthdate in day/mo/year format */
  sprintf (nametmp, "%s %s", e->first_name, e->last_name);
  sprintf (dobtmp, "%02d/%02d/%04d", e->birthdate.day, e->birthdate.month, 
            e->birthdate.year);
  
  /* output employee */
  printf ("\n%s\n  id     : %d\n  salary : %.2f\n  dob    : %s\n"
          "  addr   : %s\n  phone  : %s\n  email  : %s\n",
          nametmp, e->id, e->salary, dobtmp, e->address, 
          e->phone_number, e->email);
}

int main (int argc, char **argv) {
  
  char buf[MAXC];       /* buffer to hold each line read from file */
  int n = 0;
  employee e[C100];
  
  /* use filename provided as 1st argument (stdin by default) */
  FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

  if (!fp) {  /* validate file open for reading */
    perror ("file open failed");
    return 1;
  }
  
  /* while array not full and line read from file */
  while (n < C100 && fgets (buf, MAXC, fp)) {
    char dobtmp[C30] = "";  /* temp buffer to hold "day-mo-year" */
    
    /* parse needed info fron buf, with field-width to protect array bounds
     * VALIDATE return -- all conversions successful
     */
    if (sscanf (buf,  "%d, %19[^,], %19[^,],%f, %29[^,],"
                      " %99[^,], %19[^,], %29[^\n]",
                      &e[n].id, e[n].first_name, e[n].last_name, &e[n].salary,
                      dobtmp, e[n].address, e[n].phone_number, 
                      e[n].email) == 8) {
      
      /* separate dobtmp into birthdate, store in employee */
      if (splitdob (&e[n].birthdate, dobtmp)) {
        n++;    /* update count only after ALL validations passed */
      }
    }
  }
  
  if (fp != stdin)   /* close file if not stdin */
    fclose (fp);
  
  printf ("\n%d employees:\n", n);    /* output results */
  for (int i = 0; i < n; i++) {
    prnemployee (&e[i]);
  }
}

Example Use/Output

With your sample data in the file dat/empfile.txt you would have:

$ ./bin/emp-nested-dob dat/empfile.txt

5 employees:

Steven1 Thomas
  id     : 1
  salary : 2001.00
  dob    : 10/06/1995
  addr   : 1 Elhoreya Street
  phone  : 01234567891
  email  : 1sthomas@gmail.com

Steven2 Thomas
  id     : 2
  salary : 2002.00
  dob    : 10/06/1995
  addr   : 2 Elhoreya Street
  phone  : 01234567892
  email  : 2sthomas@gmail.com

Steven3 Thomas
  id     : 3
  salary : 2003.00
  dob    : 10/06/1995
  addr   : 3 Elhoreya Street
  phone  : 01234567893
  email  : 3sthomas@gmail.com

Steven4 Thomas
  id     : 4
  salary : 2004.00
  dob    : 10/06/1995
  addr   : 4 Elhoreya Street
  phone  : 01234567894
  email  : 4sthomas@gmail.com

Steven5 Thomas
  id     : 5
  salary : 2005.00
  dob    : 10/06/1995
  addr   : 5 Elhoreya Street
  phone  : 01234567895
  email  : 5sthomas@gmail.com

Look things over and let me know if you have further questions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM