1

I am having trouble with an assignment, it involves reading from files into structures and I'm confused on how to do it here is the function with the parameters I HAVE to use.

// This function will read in size  struct players from filename and add these
// the players array. The function will use index  to know where to start
// writing  the players to in the array.
// Parameters
//
// filename – The name of the input file
// players – a pointer to the array of player structures
// index – The index of the array to start placing players into
// size – The number of players in the input file
// Return - Nothing

void read_from_file(char* filename, Player* players, int index, int size);

This is the function I have to use to read in data from 3 DIFFERENT files that look as such:

Andrew Jackson 129 33 38 30 506
Jeremy Warden 25 24 3 9 493
Jared Welch 130 1 43 27 422
Brandon Splitter 138 38 40 7 587
Joe Gwilliams 150 23 30 25 498
Ali Mohammed 119 43 13 6 598
Dheeraj Johnson 124 79 59 36 506
Bill Clinton 121 65 12 26 449
Jesse James 87 58 8 5 464
John Doe 129 100 0 12 548

I have to read in 3 files that all have 10 players in them for a total of 30 I need to read into the structures. I have not gotten very far I know but I am very confused on what to do and how to approach this, any help would be very much appreciated! Below I have down what I have already done. Please help!! Thanks

//Brady Webb
//lab D
//HW1

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

typedef struct player
{
    char Fname[25];
    char Lname[25];
    int Singles;
    int Doubles;
    int Triples;
    int Homeruns;
    int At_Bats;
    float Slugging_Percentage;
} Player;

void read_from_file(char* filename, Player* players, int index, int size);

int main(int argc, char* argv[])
{
    int size= atoi(*(argv+1));
    char* file1 = *(argv+2);
    char* file2 = *(argv+3);
    char* file3 = *(argv+4);
    if (argc<6 || argc>6)
    {
        printf("Incorrect command line arguments\n");
        return 0;
    }

    return 0;
}
void read_from_file(char*filename, Player* players, int index, int size)
{
    FILE *ptr;
    int i=0;

    if ((ptr=fopen(filename, "r")) == NULL)
    {
        return 0;
    }

    while (ptr != EOF)
    {

    }

}
CaptainDaVinci
  • 975
  • 7
  • 23
Brady Webb
  • 23
  • 4
  • What about `argv+5`? what is the meaning of the value gotten from `argv+1`? This line: `if (argc<6 || argc>6) would be much clearer as: `if (argc != 6)` – user3629249 Feb 10 '17 at 00:22
  • error messages should be output to `stderr` rather than `stdout`. Therefore, this line: `printf("Incorrect command line arguments\n");` would be better written as: `fprintf( stderr, "Incorrect command line arguments\n");` – user3629249 Feb 10 '17 at 00:24
  • in reality, to avoid accessing memory that does not belong to the program, check the value in `argc` FIRST, and only if valid then retrieve the individual arguments. – user3629249 Feb 10 '17 at 00:26
  • When the main() function returns 0, that indicates 'success'. Strongly suggest 1) use the values defined in `stdlib.h` of `EXIT_SUCCESS` and `EXIT_FAILURE`, where an error exit should be returning `EXIT_FAILURE`. – user3629249 Feb 10 '17 at 00:28
  • when outputting an error message about the command line arguments, it is best to demonstrate what the correct arguments should look like. I.E. `fprintf( stderr, "USAGE: %s ... \n", argv[0] );` – user3629249 Feb 10 '17 at 00:31

3 Answers3

2

The simplest approach to read file with regular structure is to use fscanf with complex format string.

fscanf("%s %s %d %d %d %d %d", Player.Fname, Player.Lname, 
                          &Player.Singles, &Player.Doubles, 
                          &Player.Triples, &Player.Homeruns, &Player.At_Bats);

You should make a loop to read till the and of file, and you can add the check of reading the data in the correct format, e.g.:

int check = fscanf("%s %s %d %d %d %d %d", Player.Fname, Player.Lname, 
                          &Player.Singles, &Player.Doubles, 
                          &Player.Triples, &Player.Homeruns, &Player.At_Bats);
if( check != 7 )
{
   // stop reading and report on wrong file format 
}

UPDATE:

I propose the following code as possible solution:

// This function will read in size  struct players from filename and add these
// the players array. The function will use index  to know where to start
// writing  the players to in the array.
// Parameters
//
// filename – The name of the input file
// players – a pointer to the array of player structures
// index – The index of the array to start placing players into
// size – The number of players in the input file
// Return - number of read players (positive number)
//          or error code (negarive number)
int read_from_file(char * filename, Player* players, int index, int size)
{
    struct player ptmp;
    FILE *fptr;
    // open the file
    if ((fptr = fopen(filename, "r")) == NULL)
    {
        fprintf(stderr, "File %s cannot be oppened\n",filename);
        return -1; // error code for "File cannot be oppened"
    }
    // reading from file 
    int position = index;
    int cnt = 0;
    while (!ferror(fptr) && cnt < size)
    {
        int check = fscanf(fptr, "%24s %24s %d %d %d %d %d", ptmp.Fname, ptmp.Lname,
            &ptmp.Singles, &ptmp.Doubles, &ptmp.Triples, &ptmp.Homeruns, &ptmp.At_Bats);
        if (feof(fptr) && check != 7)
        {
            break;
        }
        if (check != 7)
        {
            fclose(fptr);
            fprintf(stderr,"Wrong data format in line %d of file %s\n", cnt+1, filename);
            return -2; // error code for "File has wrong data format"
        }
        // copy data to players
        players[index++] = ptmp;
        cnt++;
    }
    // close the file
    fclose(fptr);
    return cnt;
}

Pay attention at changed type of function read_from_file - I described my idea concerning return value in the comments.

And main in my understanding should be like:

int main(int argc, char* argv[])
{
    Player players[30]; // memory is allocated for particular number of data items
    // check the command line arguments
    if (argc < 3)
    {
        printf("Please run the program in the format:\n");
        printf(" %s 2 firstFile.txt secondFile.txt\n", argv[0]);
        printf(" where 2 is number of files given after 2 with data to be read\n");
        return 0;
    }
    int fileNumber = 0;
    if (!sscanf(argv[1], "%d", &fileNumber) || fileNumber <= 0)
    {
        printf("The first command line argument nust be positive number.\n");
        printf("Run program without parameters to see details\n");
        return 0;
    }
    if (fileNumber != (argc - 2))
    {
        printf("Command line arguments are inconsistent\n");
        printf("Run program without parameters to see details\n");
        return 0;
    }
    // file processing
    int i = 0;
    int total = 0;
    int max = 30;
    for (i = 0; i < fileNumber; i++)
    {
        printf("Reading from %s...\n", argv[i + 2]);
        int res = read_from_file(argv[i + 2], players, total, max); 
        if (res > 0)
        {
            total += res;
            max -= res;
        }
    }
    // check data
    for (i = 0; i < total; i++)
    {
        printf("%s %s : %d %d %d %d %d\n", players[i].Fname, players[i].Lname, players[i].Singles, players[i].Doubles, players[i].Triples, players[i].Homeruns, players[i].At_Bats);
    }
    return 0;
}

It is assumed that 30 players can be read any number of files, and not necessarily to 10 of each file.

VolAnd
  • 6,367
  • 3
  • 25
  • 43
  • Brady Webb should decide how loop will be written. Actually if `feof(fptr)` is used in the complex condition with other checks it looks not so wrong – VolAnd Feb 09 '17 at 08:07
  • In general, NEVER use `feof()` as it is only asserted when having already read to the end of the file, then trying read past the end of the file (on the next read) Note: there is nothing in the question, nor in the posted code that indicates that the `size` command line parameter is to indicate the number of following file names. Strongly suggest NOT making that assumption – user3629249 Feb 10 '17 at 00:38
  • astrongly suggest using the returned value from the call to `fscanf()` as the loop control and eliminating the call to `ferror()` and `feof()`, etc – user3629249 Feb 10 '17 at 00:43
  • the function: `sscanf()` returns EOF on the end of the string, not necessarily 0, – user3629249 Feb 10 '17 at 00:47
  • in the scenario your posted answer is using, the value of `argc` would be 4, not 3 – user3629249 Feb 10 '17 at 00:49
  • returning 0 from main() indicates SUCCESS. you really want, when there is an error, to return (from stdlib.h) EXIT_FAILURE. – user3629249 Feb 10 '17 at 00:51
  • when a system function, like `fopen()` fails, should use `perror( "fopen for .... failed" ); As that function will output the enclosed text AND the appropriate error message that indicates why the OS thinks the call to `fopen()` failed – user3629249 Feb 10 '17 at 00:53
  • the call to `fscanf()` has the wrong parameters. The first parameter needs to be a `FILE*`, not the format specification for the first parameter. so this line: `fscanf("%s %s %d %d %d %d %d", Player.Fname, Player.Lname, &Player.Singles, &Player.Doubles, &Player.Triples, &Player.Homeruns, &Player.At_Bats);` is wrong – user3629249 Feb 10 '17 at 00:57
0

The loop can be return to read one line at a time and each line can be passed to a helper function like getPlayer

char *line;
char buffer[256]; // assuming that's the longest line size
while ((line = fgets(buffer, sizeof(buffer), ptr)) != NULL) {
    Player *pl = getPlayer(line);
    //...
}

The above code should replace loop in the code posted as while (ptr != EOF). The getPlayer can be something along the following lines (the player can then be added to list/array etc. and make sure you free up accordingly when you are done)

Player *getPlayer(char *line) {
        Player *iPlayer = (Player *)malloc(sizeof(Player));
        sscanf(line, "%s %s %d %d %d %d %d %f",
                iPlayer->Fname,
                iPlayer->Lname,
                &iPlayer->Singles,
                &iPlayer->Doubles,
                &iPlayer->Triples,
                &iPlayer->Homeruns,
                &iPlayer->At_Bats,
                &iPlayer->Slugging_Percentage);
         return iPlayer;
}
Aniruddh Dikhit
  • 662
  • 4
  • 10
0
struct Player PlayerArr[10];

void read_from_file(char*filename, Player* players, int index, int size)
{
    FILE *ptr;
    int i=0;
    char content[50];

    memset(content, '\0', 50);

    if ((ptr=fopen(filename, "r")) == NULL)
    {
        return 0;
    }

    while (ptr != EOF)
    {
         getline(infile,line);
         memset(content,'\0',sizeof(content));
         strcpy(content,line.c_str());
         ReadNextConfRecord(content);
    }

    return 0;
}

int ReadNextConfRecord(char *content)
{
        char str[50];
    const char delimiter[2] = " ";
    char *token;
    int count =0;
    int i =0;

    memset(str, '\0', 50);

    strcpy(str, (char*)content);

    token = strtok(str, delimiter);
    while( token != NULL )
    {
     count++;
        if(count == 1)
            {
                strcpy(PlayerArr[i].Fname, token);
            }
        else if(count == 2)
            {
                strcpy(PlayerArr[i].Lname, token);
            }
        else if(count == 3)
            {
                PlayerArr[i].Singles= atoi(token);
            }
        else if(count == 4)
            {
               PlayerArr[i].Doubles= atoi(token);
            }
        else if(count == 5)
            {
               PlayerArr[i].Triples= atoi(token);
            }
        else if(count == 6)
            {
               PlayerArr[i].Homeruns= atoi(token);
            }
        else if(count == 7)
            {
               PlayerArr[i].At_Bats= atoi(token);
            }
        else if(count == 8)
            {
               PlayerArr[i].Slugging_Percentage= atof(token);
            }
       i++;                       
      token = strtok(NULL, delimiter);
    }
    return 0;
}

Using tokenizer can also solve above problem

Tom
  • 4,257
  • 6
  • 33
  • 49
Vandit Jain
  • 91
  • 1
  • 5