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)
{
}
}
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.
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;
}
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
Related
Can you guys please check this and tell me what am I doing wrong?
I am trying to read from a txt into a struct
The file looks like this: (name on a line, followed by 4 attributes on the next line)
NAME SURNAME OTHER
a1 a2 a4 a3
OTHER ANOTHER
s2 s3 s4 s5
I have to read the file into a struct, then search for a name and print the number of comparisons it took till you found it (the names in the file are alphabetically sorted - I guess binary search is optimal).
Here is what I wrote for now:
*note: I know for certain that the file will have 10000 lines and the full name has at most 40 chars
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct student
{
char name[40];
char arg1[10];
char arg2[10];
char arg3[10];
char arg4[10];
}STUD;
STUD studies[5000];
STUD *ptr = studies;
void readlines(FILE *f)
{
char line[40];
char secondline[40];
int i=0;
while(fgets(line, 40, f) != NULL) //read first line -> here has to be student's name
{
sscanf(line, "%[^\n]%*c\n", studies[i].name); //store first line ->student's name
fgets(secondline, 40, f); //read second line -> here has to be additional information
sscanf(secondline, "%s %s %s %s", studies[i].arg1, studies[i].arg2, studies[i].arg3, studies[i].arg4);
i++;
}
}
void binary_search_string(STUD *arr, char x[])
{
//printf("sizeof: %ld\n", sizeof(arr));
int l=0;
int r = 4999;
int mid;
int compare=0;
while(l <= r)
{
mid = (l + r)/2;
printf("%s\n", studies[mid].name);
if(strcmp(studies[mid].name, x) == 0)
{
compare++;
printf("%d comparisons to find %s\n", compare, x);
break;
}
else
{
if(strcmp(studies[mid].name, x) < 0)
{
compare++;
l = mid + 1;
}
else
{
compare++;
r = mid - 1;
}
}
//printf("compare: %d\n", compare);
}
}
void print(STUD *array)
{
for(int i=0; i<sizeof(array); i++)
{
printf("%s %s %s %s %s\n", studies[i].name, studies[i].arg1, studies[i].arg2, studies[i].arg3, studies[i].arg4);
}
}
int main(int argc, char **argv)
{
if(argc < 2)
{
printf("usage: ./exe file_to_open\n");
exit(-1);
}
FILE *fp = fopen(argv[1], "r");
if(fp == NULL)
{
return 1;
}
char x[]="SURNAME NAME";
readlines(fp);
print(studies);
binary_search_string(studies, x);
fclose(fp);
return 0;
}
Another thing I am not sure about is how would the function "readlines" look like when using the pointer to the array of struct (ptr). (Can you guys give me an example here?)
I am sorry in advance if this is a stupid question.
Thank you!
I am trying to read inputs from a file that contain strings such as "Jane 30", "Chris 40", and so on, line by line. I then need to store each name with it's corresponding number in the same index of different arrays, so "Jane" in one array with index 0, and 30 in an integer array with index 0.
This is the code I have so far, but I am struggling to figure out how to extract the integers into a separate array, and the characters into another. Please help.
#include stdio.h
#include stdlib.h
#include DarrensInfo.
int main()
{
FILE * coinfile;
coinfile = fopen("coins.txt", "r");
char names[150];
int change[50];
int x, y;
while(!feof(coinfile)){
fgets(names, 150, coinfile);
y = 0;
for(x=0; names[x]; x++){
if(names[x] <= '0' && names[x] <= '9'){
change[y] = names[x];
y++;
}
}
}
fclose(coinfile);
return 0;
}
#define COINS_MAX_LINES 150
#define MAX_LINE_LENGTH 100
#define MAX_NAME_LENGTH 50
int main()
{
FILE * coinfile;
coinfile = fopen("coins.txt", "r");
char line[MAX_LINE_LENGTH];
char names[COINS_MAX_LINES][MAX_NAME_LENGTH];
int change[COINS_MAX_LINES];
int lineno = 0;
int i = 0;
while(fgets(line, MAX_LINE_LENGTH, coinfile))
{
sscanf(line, "%s %d", names[lineno], &change[lineno]);
++lineno;
}
fclose(coinfile);
for (i = 0; i<lineno;++i)
printf("Name = %s Change = %d\n", names[i], change[i]);
return 0;
}
After the end of the while loop, names array & change arrays will contain what you want. I have printed it out in the second loop
You need a array of character arrays. Define the variable names as names[150][30], assuming the length of each name will not exceed more than 30 characters.
Use a combination of fgets and fscanf to parse the line of entry in the file into separate variables as you desired
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE * coinfile;
coinfile = fopen("coins.txt", "r");
char names[150][30];
int change[150];
int x = 0, y, i;
char buf[100];
while(!feof(coinfile)){
if(fgets(buf, 100, coinfile) != NULL) {
sscanf(buf, "%s %d", names[x], &change[x]);
x++;
}
}
fclose(coinfile);
puts("-------");
for (int i = 0; i < x; i++)
printf("%s %d\n", names[i], change[i]);
return 0;
}
I am trying to read inputs from a file that contain strings such as "Jane 30", "Chris 40", and so on, line by line
You're reading a file that might contain strings such as "Jane 30", "Chris 40", and so on; but might also contain millions of typing mistakes and/or other errors; and therefore you need to detect errors and clearly inform the user what the error is so that they can easily understand the problem, then find the mistake, then fix it.
For this reason none of the C library functions are ever useful.
Instead build a parser as a finite state machine. For example (untested):
// State
int state = 0;
int column = 0;
int line = 1;
char current_name[MAX_NAME_LENGTH];
int name_length;
int number;
// Main loop
for(;;) {
int c = fgetc(file);
column++;
switch(state) {
case 0: /* At start of new line */
if(c == FEOF) {
return OK;
} else if(isdigit(c)) {
printf("ERROR: Number found at start of line (missing name), on line %d at column %d\n", line, column);
return NOT_OK;
} else if(isalpha(c)) {
name_length = 0;
current_name[name_length++] = c;
state = 1;
} else if(c == '\n') {
line++
} else if(isspace(c)) {
} else {
printf("ERROR: Bad character at start of line, on line %d at column %d\n", line, column);
return NOT_OK;
}
break;
case 1: /* In middle of name */
if(c == FEOF) {
printf("ERROR: File ends in the middle of a name, on line %d at column %d\n", line, column);
return NOT_OK;
} else if(isdigit(c)) {
printf("ERROR: No whitespace between name and number, on line %d at column %d\n", line, column);
return NOT_OK;
} else if(isalpha(c)) {
if(name_length >= MAX_NAME_LENGTH) {
printf("ERROR: Name too long (max length is %d), on line %d at column %d\n", MAX_NAME_LENGTH, line, column);
return NOT_OK;
}
current_name[name_length++] = c;
} else if(c == '\n') {
printf("ERROR: No number after name, on line %d at column %d\n", line, column);
return NOT_OK;
} else if(isspace(c)) {
state = 2;
} else {
printf("ERROR: Bad character in middle of name, on line %d at column %d\n", line, column);
return NOT_OK;
}
break;
case 2: /* Between name and number */
if(c == FEOF) {
printf("ERROR: File ends after name, on line %d at column %d\n", line, column);
return NOT_OK;
} else if(isdigit(c)) {
number = c - '0';
state = 3;
} else if(c == '\n') {
printf("ERROR: No number after name, on line %d at column %d\n", line, column);
return NOT_OK;
} else if(isspace(c)) {
} else {
printf("ERROR: Bad character after name, on line %d at column %d\n", line, column);
return NOT_OK;
}
break;
case 4: /* In middle of number */
if(c == FEOF) {
printf("ERROR: File ends in middle of number, on line %d at column %d\n", line, column);
return NOT_OK;
} else if(isdigit(c)) {
if(number > INT_MAX / 10) {
printf("ERROR: Number is too large, on line %d at column %d\n", line, column);
return NOT_OK;
}
number *= 10;
if(number > INT_MAX - (c - '0') ) {
printf("ERROR: Number is too large, on line %d at column %d\n", line, column);
return NOT_OK;
}
number += c - '0';
} else if(c == '\n') {
create_new_entry(current_name, name_length, number);
line++
state = 0;
} else if(isspace(c)) {
state = 5;
} else {
printf("ERROR: Bad character after number, on line %d at column %d\n", line, column);
return NOT_OK;
}
break;
case 5: /* Trailing white space before end of line */
if(c == FEOF) {
printf("ERROR: File ends between number and end of line, on line %d at column %d\n", line, column);
return NOT_OK;
} else if(c == '\n') {
line++
create_new_entry(current_name, name_length, number);
state = 0;
} else if(isspace(c)) {
} else {
printf("ERROR: Unknown characters between number and end of line, on line %d at column %d\n", line, column);
return NOT_OK;
}
}
}
If you're trying to parse names that might look like "King Charles 3", it will be difficult to use scanf. It's not actually that difficult to do this sort of thing without using static sized buffers, and that's a good practice to get used to. You'll want to avoid fgets, since that is difficult to use without a fixed size. Note that growing arrays is notoriously difficult, so I make no claims as to the correctness of the following. One has to do this sort of exercise every few months to be reminded why we don't do this sort of thing in C:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
FILE * Fopen(const char *, const char *);
void * Realloc(void *, size_t);
/* Remove trailing whitespace */
void trim_white(char *b, char *end)
{
while( end > b && isspace(*--end)) {
*end = '\0';
}
}
int
main(int argc, char **argv)
{
char *path = argc > 1 ? argv[1] : "stdin";
FILE *coinfile = argc > 1 ? Fopen(path, "r") : stdin;
size_t asize = 16;
size_t bsize = 0;
char *buf = NULL;
char **names = Realloc(NULL, asize * sizeof *names);
long *change = Realloc(NULL, asize * sizeof *change);
unsigned line_number = 0;
ssize_t char_read;
while( buf = NULL, (char_read = getline(&buf, &bsize, coinfile)) != -1) {
char *space;
char *end;
trim_white(buf, buf + char_read);
space = strrchr(buf, ' ');
if(space == NULL) {
fprintf(stderr, "Invalid input on line %d (no space)\n", line_number + 1);
exit(EXIT_FAILURE);
}
change[line_number] = strtol(space + 1, &end, 10);
if(*end != '\0') {
fprintf(stderr, "Invalid input on line %d at '%s'\n", line_number + 1, space + 1);
exit(EXIT_FAILURE);
}
*space = '\0';
names[line_number] = buf;
if(++line_number == asize) {
asize *= 2;
names = Realloc(names, asize * sizeof *names);
change = Realloc(change, asize * sizeof *change);
}
}
return EXIT_SUCCESS;
}
FILE *
Fopen(const char *path, const char *mode) {
FILE *fp = fopen(path, mode);
if( fp == NULL ) { perror(path); exit(EXIT_FAILURE); }
return fp;
}
void *
Realloc(void *buf, size_t s)
{
buf = realloc( buf, s );
if( buf == NULL) { perror("realloc"); exit(EXIT_FAILURE); }
return buf;
}
Hey I have a problem with my code project where I try to create a project that keeps up wit Olympic medals. I have a problem of creating a text file that contains the structure and is named by user. I also have a problem to download the structure.
PROBLEM: I have a problem to make a text file named by user that contains the structure and then download it back to stdout. I don't know how to fix my functions to do this correctly. Now my function save_file can't even produce the file.
Example if the input:
A Canada
A USA
M USA 2 1 1
M Canada 0 0 1
M USA 1 3 1
M USA -1 0 0
L
W medals
Q
I have defined my structure this way:
typedef struct Olympia
{
char* country;
int gold;
int silver;
int bronze;
}Olympia;
Then I have a function that adds country
int add_country(struct Olympia* data, char* str, int i)
{
if (str[0] == '\0') //checking that input is correct
{
printf("Error! Try again!\n");
}
else
{
data[i].country = malloc(strlen(str) + 2); //allocating memory for country name
strcpy(data[i].country, str); //adding country to database
data[i].gold = 0; //setting medals to zero
data[i].silver = 0;
data[i].bronze = 0;
i++;
}
return i;
}
Next I add medals to the each country
int update_medals(struct Olympia* data, char* str, int add_gold, int add_silver, int add_bronze, int i)
{
int a = 0;
int b = 0;
if (str[0] == '\0') //checking that input is correct
{
printf("Error! Try again!");
}
else
{
while (a < i)
{
if (strcmp(data[a].country, str) == 0) //adding medals to right country
{
data[a].gold = data[a].gold + add_gold;
data[a].silver = data[a].silver + add_silver;
data[a].bronze = data[a].bronze + add_bronze;
b++;
}
a++;
}
if (b == 0) //and if the country didn't participate to the olympics
{
printf("This country isn't in the Olympics! Try Again!\n");
}
}
}
Next there is print function
int print_data(struct Olympia* data, int i)
{
for (int a = 0; a < i; a++)
{
printf("%s %d %d %d\n", data[a].country, data[a].gold, data[a].silver, data[a].bronze);
}
}
And then there are the two function that doesn't work. What should I do?
Olympia *save_file(Olympia* data, const char* filename, int i)
{
if (strlen(filename) > 100)
{
printf("Filename is too long: Maxium lenght for filename is 100 characters");
return data;
}
char name[100];
int ret = sscanf(filename, "W %s", name);
if (ret != 1)
{
printf("Error! Try again!");
return data;
}
FILE* file = fopen(name, "w");
if (!file)
{
printf("Error saving file! Try again");
return data;
}
int a = 0;
while (data[a].country[0] != 0)
{
fprintf(file, "%s %d %d %d\n", data[a].country, data[a].gold, data[a].silver, data[a].bronze);
a++;
}
fclose(file);
return 0;
}
int load_file(struct Olympia* data, char* filename, int i)
{
int a = 0;
FILE* file = fopen(filename, "r");
if (!file)
{
printf("Error opening file! Try again");
}
struct Olympia* arr = malloc(sizeof(Olympia));
while (fscanf(file, "%s %d %d %d", data[a].country, data[a].gold, data[a].silver, data[a].bronze))
{
i++;
a++;
arr = realloc(arr, sizeof(Olympia) * (i + 2));
}
arr[a].country[0] = 0;
fclose(file);
return arr;
}
And the main function
int main(void)
{
char command;
int gold = 0;
int silver = 0;
int bronze = 0;
int i = 0;
char* line = (char*)malloc((100) * sizeof(char)); //allocating memory for one stdin line
char* countryname = (char*)malloc(20 * sizeof(char)); // allocating memory for country name
char* filename = (char*)malloc(100 * sizeof(char));
struct Olympia* countrydata = malloc(sizeof(struct Olympia) * 1); //allocating memory for structure
line = fgets(line, 100, stdin);
while(1)
{
sscanf(line, "%c %s %d %d %d", &command, countryname, &gold, &silver, &bronze);
switch (command)
{
case 'A':
i = add_country(countrydata, countryname, i);
countrydata = realloc(countrydata, sizeof(struct Olympia) * (i + 1));
break;
case 'M':
update_medals(countrydata, countryname, gold, silver, bronze, i);
break;
case 'L':
print_data(countrydata, i);
break;
case 'W':
save_file(countrydata, filename, i);
break;
case 'O':
i = load_file(countrydata,filename, i);
break;
case 'Q':
free(line);
free(countryname);
free(countrydata);
return(EXIT_SUCCESS);
}
line = fgets(line, 100, stdin);
if (line == NULL)
{
free(line);
free(countryname);
free(countrydata);
return(EXIT_SUCCESS);
}
}
}
You call save_file(countrydata, filename, i); without having set filename. Change to save_file(countrydata, line, i); since for whatever reason you expect the command character W to precede the name.
Then in save_file() the condition in while (data[a].country[0] != 0) is unusable, since the data element after the last one is not initialized. Use while (a < i) instead.
int main() {
FILE *fp = fopen("fileA.txt", "r"); /* read file */
int i = 0;
char name[200][100];
char goods[200][100];
char qty[200][100];
char temp[200][100];
int x = 0;
int result;
while (!feof(fp)) {
fscanf(fp, "%[^,] , %[^,] , %s " , name[i], item[i], qty[i]); /*get file content and store in array */
if (strcmp(item[i], "Football") == 0) { /* only select Football */
temp[x][x] = qty[i];
if (x > 0) {
if (strcmp(temp[x][x], temp[x + 1][x + 1]) > 0) { /*compare who has more football qty */
result = x; /*output the person who have more football*/
}
}
x = x + 1;
}
}
printf("%s is team leader in class.\n", name[result]);
fclose(fp);
getchar();
return 0;
}
Hi all, I don't know why the result not correct.
I want to find out who has more football and print out his/her name.
Seems something wrong on if (strcmp(temp[x], temp[x + 1]) > 0)
I am not clearly on using pointer and address.
the content in the text file are:
Alice,Eating,001
Kitty,Football,006
Ben,Swimming,003
May,Football,004
And I expect the result is :
Kitty is team leader in class.
Thank you.
There are multiple problems in your code:
you do not test if the file is properly open.
you cannot properly parse a file with while (!feof(fp)) {. You should iterate for as long as fscanf() returns 3, or preferably read the input line by line and parse it with sscanf().
you do not tell fscanf() the maximum number of characters to store into the destination arrays. This may cause undefined behavior for invalid input.
you do not increment i for each line read. Every line of input overwrites the previous one.
you do not check if there are more than 200 lines. Undefined behavior in this case.
Your test to find the football fan with the highest quantity is broken: no need for a 2D array here, just keep track of the current maximum and update it when needed.
Here is a modified version:
#include <stdio.h>
int main() {
FILE *fp = fopen("fileA.txt", "r"); /* read file */
char buf[300];
char name[200][100];
char goods[200][100];
char qty[200][100];
int i, qty, max_qty = 0, result = -1;
if (fp == NULL) {
fprintf(stderr, "cannot open file\n");
return 1;
}
for (i = 0; i < 200; i++) {
if (!fgets(buf, sizeof buf, fp))
break;
if (sscanf(buf, " %99[^,], %99[^,],%99s", name[i], item[i], qty[i]) != 3) {
fprintf(stderr, "invalid input: %s\n", buf);
break;
}
if (strcmp(item[i], "Football") == 0) { /* only select Football */
qty = atoi(qty[i]);
if (result == -1 || qty > max_qty) {
result = i; /*store the index of the person who have more football */
}
}
}
if (result < 0)
printf("no Football fan at all!\n");
else
printf("%s is team leader in class with %d in Football.\n", name[result], max_qty);
fclose(fp);
getchar();
return 0;
}
Above Code is not clear as what you want to do in this code block
if ( strcmp(temp [x], temp [x+1]) > 0 ){ /* when matches, accessing temp[x+1] results in undefined behaviour */
result = x;
}
also why char *temp[200][100]; as to store qty[i], char *temp is enough or you can take char temp[200][100];
Here is somewhat better one as requirement is not clear.
int main() {
FILE *fp= fopen("fileA.txt","r"); /* read file */
if(fp == NULL) {
/* not exist.. write something ?? */
return 0;
}
char name [200][100],goods[200][100],qty[200][100],temp[200][100];
int x = 0,result = 0, i = 0;
while ((fscanf(fp, "%[^,] , %[^,] , %s " , name[i], goods [i], qty[i])) == 3) {
if (strcmp(goods[i] , "Football") == 0){
strcpy(temp[x],qty[i]);
if ( strcmp(temp [x], temp [x+1]) > 0 ) { /* UB ? */
result = x;
x+=1;
}
}
}
printf("%s is team leader in class. \n", name[result]);
fclose(fp);
getchar();
return 0;
}
When running this code with an input .txt file containing somewhere between 200-300 integers (separated by spaces) i get an error right before the for loop with the fprintf statement.
I am not sure if qsort is causing this error or why it occurs but any insight would be appreciated.
(this file is run by adding the name of the input file and the output file in the command line ex: ./program input.txt output.txt
My code:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
int cmpfunc (const void * a, const void * b)
{
return ( *(int*)a - *(int*)b );
}
int main(int argc, char *argv[]){
if(argc != 3){
printf("\nInvalid input\nPlease provide the input and output text file names as %s name1 name2\n", argv[0]);
}else{
printf("\nPart A: \n");
printf("..............................................................................................................\n\n");
char *fn1 = argv[1]; //variables
char *fn2 = argv[2];
int temp = 0;
int counter = 0;
int index = 0;
int index2 = 0;
int sort = 0;
FILE *fp1 = fopen(fn1, "r"); //read file
FILE *fp2 = fopen(fn2, "w"); //write file
if(fp1 == NULL){ //test if fp1 was opened
printf("There was an error opening the input file");
}
char data[10]; //ints can only hold 10 digits
int *integerArr;
int *tempPointer;
integerArr = malloc(10*sizeof(int));
int sizeOfArrs = 10;
printf("Reading in the textfile: ");
while(fscanf(fp1,"%s",data) != EOF){ //reads in the file breaking on each whitespace and ends at the EOF pointer
temp = strlen(data);
if(temp <=10){
temp = atoi(data);
integerArr[counter] = temp;
printf(".");
counter++;
if(counter == sizeOfArrs -1){
temp = sizeOfArrs * 2;
tempPointer = realloc(integerArr, temp);
if(tempPointer != NULL){
integerArr = tempPointer;
}
}
}else printf("\ninteger had too many digits\n");
}
printf(" Done\n%d Numbers were found\n", counter);
printf("The integers found in the %s file: \n", argv[1]);
index = 0; //reset index to 0;
for(index;index<counter;index++){ //prints the unsorted contents of the file
printf("%d ", integerArr[index]);
}
printf("\n\nPart B\n");
printf("..............................................................................................................\n\n");
printf("The integers found in the %s file after sorting: \n", argv[1]);
qsort(integerArr, counter, sizeof(int), cmpfunc); //best function ever (sorts the array using the cmpfunc to tell if an integer is greater than less than or equal to the next one)
index = 0; //resets the index
for(index; index <counter; index++){ //prints the sorted contents of the file
printf("%d ", integerArr[index]);
fprintf(fp2,"%d ",integerArr[index]); //writes the sorted integers to the new file
}
if(fp2 == NULL){ //tests if the write worked
printf("There was an error writing the outputfile");
}
printf("\n");
close(fp1,fp2); //closes both files
}
return 0;
}
Your fscanf loop is broken. You weren't actually realloc'ing with a larger size. Here's the corrected program [sorry for the pedantic style reedit but you hit one of my nits: long sidebar comments]
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
int
cmpfunc(const void *a, const void *b)
{
return (*(int *) a - *(int *) b);
}
int
main(int argc, char *argv[])
{
if (argc != 3) {
printf("\nInvalid input\nPlease provide the input and output text file names as %s name1 name2\n", argv[0]);
return 1;
}
printf("\nPart A: \n");
printf("..............................................................................................................\n\n");
char *fn1 = argv[1]; // variables
char *fn2 = argv[2];
int temp = 0;
int counter = 0;
int index = 0;
int index2 = 0;
int sort = 0;
FILE *fp1 = fopen(fn1, "r");
FILE *fp2 = fopen(fn2, "w");
// test if fp1 was opened
if (fp1 == NULL) {
printf("There was an error opening the input file");
return 1;
}
// ints can only hold 10 digits
char data[10];
int *integerArr;
int *tempPointer;
int sizeOfArrs = 10;
integerArr = malloc(sizeOfArrs * sizeof(int));
printf("Reading in the textfile: ");
// reads in the file breaking on each whitespace and ends at the EOF
// pointer
while (fscanf(fp1, "%s", data) != EOF) {
temp = strlen(data);
if (temp > 10) {
printf("\ninteger had too many digits\n");
continue;
}
temp = atoi(data);
integerArr[counter] = temp;
printf(".");
counter++;
if (counter == sizeOfArrs - 1) {
sizeOfArrs += 600;
integerArr = realloc(integerArr, sizeOfArrs * sizeof(int));
}
}
// trim array to actual size needed
sizeOfArrs = counter;
integerArr = realloc(integerArr, sizeOfArrs * sizeof(int));
printf(" Done\n%d Numbers were found\n", counter);
printf("The integers found in the %s file: \n", argv[1]);
// prints the unsorted contents of the file
for (index = 0; index < counter; index++) {
printf("%d ", integerArr[index]);
}
printf("\n\nPart B\n");
printf("..............................................................................................................\n\n");
printf("The integers found in the %s file after sorting: \n", argv[1]);
// best function ever (sorts the array using the cmpfunc to tell if an
// integer is greater than less than or equal to the next one)
qsort(integerArr, counter, sizeof(int), cmpfunc);
// prints the sorted contents of the file
for (index = 0; index < counter; index++) {
printf("%d ", integerArr[index]);
// writes the sorted integers to the new file
fprintf(fp2, "%d ", integerArr[index]);
}
// tests if the write worked
if (fp2 == NULL) {
printf("There was an error writing the outputfile");
}
printf("\n");
// closes both files
fclose(fp1);
fclose(fp2);
return 0;
}
Also, note the fclose's at the bottom. There are a few minor bugs left for you to find.