fscanf() causing segmentation fault - c

I am writing some code in C to read some file data in arrays and keep getting a segmentation fault compiling with gcc. It reads the file up to 11th line of data then gives the fault. Been through some other similar questions on here but can't find a solution.
Thanks
code:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
int ainb(char a[],char b[])//returns 0 if str b contains a returns 1 otherwise
{
int i=0,j0=-1,j1=0,count=0;
if(strlen(b)<strlen(a)) {return 1;}
for(i=0;i<strlen(b);i++) {
if((b[i]==a[j1])&&(j1==j0+1)){
j0=j1;j1++;
} else {
j0=-1;j1=0;
}
if((j1+1)==strlen(a)) {break;}
}
if((j1+1)==strlen(a)){
return 0;
} else {
return 1;
}
}
void read_pdb(FILE* fp,char **atm,int *atnum,char **name,char **res,char *chain,int *resnum,double *x,double *y,double *z,double *occ,double *bfac,char **elem,double ac[2][3]) //reads file lines and stores in arrays
{
printf("\nReading pdb data\n");
int i=0,j=0;
char buff[7];
fpos_t position;
while(!feof(fp))
{
fgetpos(fp,&position);fgets(buff,sizeof buff,fp);
if((ainb("ATOM",buff)==0)||(ainb("HETATM",buff)==0))
{
fsetpos(fp,&position);printf("\ngetting position %d\n",i+1);
fscanf(fp,"%6s%5d %4s %3s %1s%4d %8lf%8lf%8lf%6lf%6lf %2s \n",atm[i],&atnum[i],name[i],res[i],&chain[i],&resnum[i],&x[i],&y[i],&z[i],&occ[i],&bfac[i],elem[i]);
printf("\nnode %d data found\n",i+1);
printf("\n%6s%5d %4s %3s %1s%4d %8.3lf%8.3lf%8.3lf%6.2lf%6.2lf %2s \n",atm[i],atnum[i],name[i],res[i],&chain[i],resnum[i],x[i],y[i],z[i],occ[i],bfac[i],elem[i]);
if(ainb("HETATM",atm[i])==0){
ac[j][0]=x[i];ac[j][1]=y[i];ac[j][2]=z[i];j++;
}
i++;
}
}
printf("\n%d Atoms read\n",i);
}
void main()
{
double ac[2][3];
int N,k;
double *x,*y,*z,*occ,*bfac;
char **atm,**name,**res,**elem,*chain;
int *atnum,*resnum;
FILE *out;
out=fopen("OUT.pdb","r");//something to check for file
N=66;
//make dynamic arrays
x=(double*)malloc(N*sizeof(double));
y=(double*)malloc(N*sizeof(double));
z=(double*)malloc(N*sizeof(double));
occ=(double*)malloc(N*sizeof(double));
bfac=(double*)malloc(N*sizeof(double));
atnum=(int*)malloc(N*sizeof(int));
resnum=(int*)malloc(N*sizeof(int));
atm=(char**)malloc(N*sizeof(char));
name=(char**)malloc(N*sizeof(char));
res=(char**)malloc(N*sizeof(char));
elem=(char**)malloc(N*sizeof(char));
chain=(char*)malloc(N*sizeof(char));
for(k=0;k<N;k++)
{
atm[k]=(char*)malloc(7*sizeof(char));
name[k]=(char*)malloc(5*sizeof(char));
res[k]=(char*)malloc(4*sizeof(char));
elem[k]=(char*)malloc(3*sizeof(char));
}
//read in data
read_pdb(out,atm,atnum,name,res,chain,resnum,x,y,z,occ,bfac,elem,ac);
fclose(out);
printf("\n-------------------------------------------\nTest Complete\n");
free(x);
free(y);
free(z);
free(occ);
free(bfac);
free(elem);
free(name);
free(atm);
free(res);
free(resnum);
free(atnum);
free(chain);
}
The output is:
Reading pdb data
getting position 1
node 1 data found
ATOM 1 CA PRO A 1 4.612 0.903 5.089 1.00 24.97 C
getting position 2
node 2 data found
ATOM 2 CA SER A 2 3.526 0.341 3.809 1.00 59.99 C
getting position 3
node 3 data found
ATOM 3 CA ARG A 3 6.208 1.550 6.551 1.00 20.40 C
getting position 4
node 4 data found
ATOM 4 CA TRP A 4 5.912 2.348 4.388 1.00 50.28 C
getting position 5
node 5 data found
ATOM 5 CA GLE A 5 4.087 4.359 6.884 1.00 54.04 C
getting position 6
node 6 data found
ATOM 6 CA THR A 6 4.405 1.292 2.566 1.00 62.06 C
getting position 7
node 7 data found
ATOM 7 CA TYR A 7 3.327 3.041 5.205 1.00 50.46 C
getting position 8
node 8 data found
ATOM 8 CA VAL A 8 5.276 0.109 0.387 1.00 58.00 C
getting position 9
node 9 data found
ATOM 9 CA LEU A 9 2.992 3.190 3.084 1.00 41.48 C
getting position 10
node 10 data found
ATOM 10 CA CYS A 10 3.565 0.287 0.721 1.00 47.65 C
getting position 11
Segmentation fault (core dumped)

Lets consider this code:
name=(char**)malloc(N*sizeof(char));
for(k=0;k<N;k++)
{
name[k]=(char*)malloc(5*sizeof(char));
}
You allocate n*sizeof(char) array and try to store N pointers to char in it. But size of pointer to the char is greater than sizeof(char), so you get buffer overflow and undefined behavior even on the initialization stage. You are lucky and your program is not crashing at this stage, but it will fail on the array usage. To prevent this error you should use sizeof(char*) in your allocation code.

Rather than hard code the type, and get it wrong for name, let the compiler figure it out. Less code, easier to read and easier to code & maintain.
//bfac=(double*)malloc(N*sizeof(double));
//resnum=(int*)malloc(N*sizeof(int));
//name=(char**)malloc(N*sizeof(char)); OP was looking for `sizeof (char*)`
bfac = malloc(N * sizeof *bfac);
resnum = malloc(N * sizeof *resnum);
name = malloc(N * sizeof *name);
Also in C, no need to cast the result of malloc().

Related

How can fscanf(), in C, be used to read a .gro file?

I am trying to read the following gro file via a C code.
FJP in Pol Water in water t= 0.00000 step= 0
16
1FJP P 1 5.346 7.418 0.319
2FJP P 2 5.151 7.405 0.499
3FJP P 3 5.260 7.178 0.428
4FJP P 4 5.159 6.961 0.342
5FJP P 5 5.355 6.909 0.220
6FJP P 6 5.169 6.824 0.043
7FJP P 7 5.068 6.669 11.454
8FJP P 8 4.919 6.861 11.482
9FJP P 9 4.835 7.075 11.364
10FJP P 10 4.738 6.987 11.197
11FJP P 11 4.847 7.115 10.993
12FJP P 12 4.642 7.126 10.870
13FJP P 13 4.680 6.940 10.674
14FJP P 14 4.521 7.052 10.545
15FJP P 15 4.321 6.973 10.513
16FJP P 16 4.315 6.728 10.516
11.56681 11.56681 11.56681
My code is:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char * argv[])
{
char input_file[]="file.gro";
FILE *input;
char *myfile=malloc(sizeof(char)*80);
sprintf(myfile,"%s",input_file); //the .gro file being read in
input=fopen(myfile,"r");
double dummy1,dummy6,dummy7,dummy8,dummy9,dummy10,dummy11;
int dummy2,dummy3,dummy4,dummy5;
int lines=0;
while (fscanf(input,"FJP in Pol Water in water t= %lf step= %d",&dummy1,&dummy2)==2
||fscanf(input," %d\n",&dummy3)==1
||fscanf(input," %dFJP P %d %lf %lf %lf\n",
&dummy4,&dummy5,&dummy6,&dummy7,&dummy8)==5
||fscanf(input," %lf %lf %lf\n",&dummy9,&dummy10,&dummy11)==3)
{
printf("%lf %d\n",dummy1,dummy2);
printf("%d\n",dummy3);
printf("%d %d\n",dummy4,dummy5);
printf("%lf %lf %lf\n",dummy6,dummy7,dummy8);
printf("%lf %lf %lf\n",dummy9,dummy10,dummy11);
lines=lines+1;
}
printf("lines=%d\n",lines);
fclose(input);
}
The problem is the values printed by the various dummy variables do not match what is in the file. Also, the number of lines being read is 3 as opposed to 19, which matches the file. I am not certain what is incorrect about my fscanf() statements to read this file. Any help for this problem would be much appreciated.
Your main problem is that you are assuming scanf is better than it is.
Scanf will read and parse as many arguments as it can, and then give up. It does not rewind to the start of the scanf. Also it treats spaces and newlines (and tabs) as simply "skip all whitespace"
So the line printf("%d\n",dummy3) will try to parse the main lines, eg 1FJP
It will read the digit 1 OK into dummy3, but then get stuck because P != a whitespace.
All the other rules will then get stuck, because none of them expect a P or any string first.
If you want to do it this way, you will just have to apply the scanf statements more intelligently as and when they are expected.
The problem is that you try to read and match the header repeatedly, before each line read (in the while loop.) you should read the head once, then read the lines. You also only need to skip any given piece of whitespace once. So you end up with code like:
if (fscanf(input,"FJP in Pol Water in water t=%lf step=%d%d", &dummy1, &dummy2, &dummy3) != 3) {
fprintf(stderr, "Invalid header\n");
exit(1); }
while (fscanf(input,"%dFJP P%d%lf%lf%lf", &dummy4, &dummy5, &dummy6, &dummy7, &dummy8) == 5) {
... read a line of the table

Adding Line break After Every Line

I have written the following piece of code:
#include<stdio.h>
void add_linebreak()
{
FILE *fp ;
char c, NEWL=10 ;
fp=fopen("text1.txt","a+") ;
if(fp==NULL)
{
printf("\nFile Not Found") ;
}
fseek(fp,0,SEEK_CURR) ;
while(!feof(fp))
{
//if(!feof(fp))
//{
c=fgetc(fp);
if(c==NEWL)
{
fprintf(fp,"%c",NEWL) ;
}
//}
}
fclose(fp) ;
}
int main()
{
add_linebreak() ;
printf("\nEditing Complete") ;
return 0 ;
}
The program took following data as input from a file named text1.txt :
1 this
2 is
3 a
4 text
5 file
6 to
7 test a
8 program
9 written
10 in c
Actual Output :
1 this
2 is
3 a
4 text
5 file
6 to
7 test a
8 program
9 written
10 in c
11
Expected Output:
1 this
2
3 is
4
5 a
6
7 text
8
9 file
10
11 to
12
13 test a
14
15 program
16
17 written
18
19 in c
20
I scratched my head on this for hours but wasn't able to get the expected output, please help me.
you gotta make a copyy of the file
Also fgetc returns int not char
void add_linebreak()
{
FILE *fin, *fout ;
char NEWL='\n' ;
fin=fopen("text1.txt","rb") ;
if(fp==NULL)
{
printf("\nFile Not Found") ;
}
fout = fopen("copy.txt","w");
while(1)
{
int c=fgetc(fin);
if(c==EOF)
break;
fprintf(fout, "%c", c);
if(c==NEWL)
{
fprintf(fout,"%c",NEWL) ;
}
}
fclose(fout) ;
fclose(fin);
}
renaming the file back to the original and adding some more error handling is left as a test :-)
You may not perform the modifications in-place. Not with stuff like text.
You have to instead read from one file, write to a temporary then perhaps rename the copy to overwrite the original update.
The other answer says almost everything besides the rename.
Oh, another thing: I have found often that newlines tended to get stripped in my old test programs; that may be another thing. But the modifying-in-place is the most likely culprit.

Having issues iterating through machine code

I'm attempting to recreate the wc command in c and having issues getting the proper number of words in any file containing machine code (core files or compiled c). The number of logged words always comes up around 90% short of the amount returned by wc.
For reference here is the project info
Compile statement
gcc -ggdb wordCount.c -o wordCount -std=c99
wordCount.c
/*
* Author(s) - Colin McGrath
* Description - Lab 3 - WC LINUX
* Date - January 28, 2015
*/
#include<stdio.h>
#include<string.h>
#include<dirent.h>
#include<sys/stat.h>
#include<ctype.h>
struct counterStruct {
int newlines;
int words;
int bt;
};
typedef struct counterStruct ct;
ct totals = {0};
struct stat st;
void wc(ct counter, char *arg)
{
printf("%6lu %6lu %6lu %s\n", counter.newlines, counter.words, counter.bt, arg);
}
void process(char *arg)
{
lstat(arg, &st);
if (S_ISDIR(st.st_mode))
{
char message[4056] = "wc: ";
strcat(message, arg);
strcat(message, ": Is a directory\n");
printf(message);
ct counter = {0};
wc(counter, arg);
}
else if (S_ISREG(st.st_mode))
{
FILE *file;
file = fopen(arg, "r");
ct currentCount = {0};
if (file != NULL)
{
char holder[65536];
while (fgets(holder, 65536, file) != NULL)
{
totals.newlines++;
currentCount.newlines++;
int c = 0;
for (int i=0; i<strlen(holder); i++)
{
if (isspace(holder[i]))
{
if (c != 0)
{
totals.words++;
currentCount.words++;
c = 0;
}
}
else
c = 1;
}
}
}
currentCount.bt = st.st_size;
totals.bt = totals.bt + st.st_size;
wc(currentCount, arg);
}
}
int main(int argc, char *argv[])
{
if (argc > 1)
{
for (int i=1; i<argc; i++)
{
//printf("%s\n", argv[i]);
process(argv[i]);
}
}
wc(totals, "total");
return 0;
}
Sample wc output:
135 742 360448 /home/cpmcgrat/53/labs/lab-2/core.22321
231 1189 192512 /home/cpmcgrat/53/labs/lab-2/core.26554
5372 40960 365441 /home/cpmcgrat/53/labs/lab-2/file
24 224 12494 /home/cpmcgrat/53/labs/lab-2/frequency
45 116 869 /home/cpmcgrat/53/labs/lab-2/frequency.c
5372 40960 365441 /home/cpmcgrat/53/labs/lab-2/lineIn
12 50 1013 /home/cpmcgrat/53/labs/lab-2/lineIn2
0 0 0 /home/cpmcgrat/53/labs/lab-2/lineOut
39 247 11225 /home/cpmcgrat/53/labs/lab-2/parseURL
138 318 2151 /home/cpmcgrat/53/labs/lab-2/parseURL.c
41 230 10942 /home/cpmcgrat/53/labs/lab-2/roman
66 162 1164 /home/cpmcgrat/53/labs/lab-2/roman.c
13 13 83 /home/cpmcgrat/53/labs/lab-2/romanIn
13 39 169 /home/cpmcgrat/53/labs/lab-2/romanOut
7 6 287 /home/cpmcgrat/53/labs/lab-2/URLs
11508 85256 1324239 total
Sample rebuild output (./wordCount):
139 76 360448 /home/cpmcgrat/53/labs/lab-2/core.22321
233 493 192512 /home/cpmcgrat/53/labs/lab-2/core.26554
5372 40960 365441 /home/cpmcgrat/53/labs/lab-2/file
25 3 12494 /home/cpmcgrat/53/labs/lab-2/frequency
45 116 869 /home/cpmcgrat/53/labs/lab-2/frequency.c
5372 40960 365441 /home/cpmcgrat/53/labs/lab-2/lineIn
12 50 1013 /home/cpmcgrat/53/labs/lab-2/lineIn2
0 0 0 /home/cpmcgrat/53/labs/lab-2/lineOut
40 6 11225 /home/cpmcgrat/53/labs/lab-2/parseURL
138 318 2151 /home/cpmcgrat/53/labs/lab-2/parseURL.c
42 3 10942 /home/cpmcgrat/53/labs/lab-2/roman
66 162 1164 /home/cpmcgrat/53/labs/lab-2/roman.c
13 13 83 /home/cpmcgrat/53/labs/lab-2/romanIn
13 39 169 /home/cpmcgrat/53/labs/lab-2/romanOut
7 6 287 /home/cpmcgrat/53/labs/lab-2/URLs
11517 83205 1324239 total
Notice the difference in the word count (second int) from the first two files (core files) as well as the roman file and parseURL files (machine code, no extension).
C strings do not store their length. They are terminated by a single NUL (0) byte.
Consequently, strlen needs to scan the entire string, character by character, until it reaches the NUL. That makes this:
for (int i=0; i<strlen(holder); i++)
desperately inefficient: for every character in holder, it needs to count all the characters in holder in order to test whether i is still in range. That transforms a simple linear Θ(N) algorithm into an Θ(N2) cycle-burner.
But in this case, it also produces the wrong result, since binary files typically include lots of NUL characters. Since strlen will actually tell you where the first NUL is, rather than how long the "line" is, you'll end up skipping a lot of bytes in the file. (On the bright side, that makes the scan quadratically faster, but computing the wrong result more rapidly is not really a win.)
You cannot use fgets to read binary files because the fgets interface doesn't tell you how much it read. You can use the Posix 2008 getline interface instead, or you can do binary input with fread, which is more efficient but will force you to count newlines yourself. (Not the worst thing in the world; you seem to be getting that count wrong, too.)
Or, of course, you could read the file one character at a time with fgetc. For a school exercise, that's not a bad solution; the resulting code is easy to write and understand, and typical implementations of fgetc are more efficient than the FUD would indicate.

Why is fscanf changing values of structure without explicitly being told to?

I am trying to read a text file into a structure in c, but on the last iteration of my fscanf() loop, it changes both the numbers and text stored in the first and some of the second parts of my structure.
Debugging has revealed that this behaviour is caused by the while fscanf() loop. Although changing the size of the strings input prevented the numbers being changed, the string on the first line PMs.Party[0] still changed from = Labour to r. Here is my code:
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
void PartyPwr( int Runs, int Time[12], char Prty[12][15]);
struct Data
{
char *Name[12][15];
int StrtMnth[12];
int StrtYr[12];
int EndMnth[12];
int EndYr[12];
char Party[12][15]; // if this is 13 20 it runs without numbers changing.
int TimePwr[12];
};
int main(void)
{
int Max=0;
int i=0;
FILE *PriMins;
struct Data PMs;
if ((PriMins=fopen("PM.txt", "r")) == NULL)
{
printf("Error: PM.txt cannot be read.");
system("pause");
return(1);
}
while(fscanf(PriMins, "%s %d %d %d %d %s", &PMs.Name[Max], &PMs.StrtMnth[Max], &PMs.StrtYr[Max], &PMs.EndMnth[Max], &PMs.EndYr[Max], &PMs.Party[Max]) > 0)
{
PMs.TimePwr[Max]=((PMs.EndMnth[Max] +(PMs.EndYr[Max]*12)) - (PMs.StrtMnth[Max] + (PMs.StrtYr[Max]*12)));
printf("%s %d Total term %d\n",PMs.Name[Max], PMs.EndMnth[Max],PMs.TimePwr[Max]);
printf("Max val, %d bug check %d, %d, Party %s\n",Max, PMs.TimePwr[0], PMs.TimePwr[1], PMs.Party[0]);
Max++;
}
//PartyPwr(Max, PMs.TimePwr, PMs.Party);
//printf("%d, %d", PMs.TimePwr[0], PMs.TimePwr[1]);
fclose(PriMins);
system("pause");
return(0);
}
void PartyPwr( int Runs , int Time[12], char Prty[12][15])
int i=0;
int LabPwr=0;
int ConPwr=0;
for (i=0;i<Runs;i++)
{
printf("%s\n", Prty[i]);
if (strcmp(Prty[i],"Labour")==0)
{
LabPwr=(LabPwr+Time[i]);
}
if (strcmp(Prty[i],"Conservative")==0)
{
ConPwr=(ConPwr+Time[i]);
}
if ((strcmp(Prty[i],"Conservative")!=0) && (strcmp(Prty[i],"Labour")!=0))
{
printf("An invalid party was present in the list.");
}
}
printf ("Total Labour time in power: %d\nTotal Conservative time in power: %d\n", LabPwr, ConPwr);
}
This is the text file for the programme.
Attlee 7 1945 10 1951 Labour
Churchill 11 1951 5 1955 Conservative
Eden 6 1955 12 1956 Conservative
Macmillan 1 1957 10 1963 Conservative
Douglas-Home 11 1963 10 1964 Conservative
Wilson 11 1964 5 1970 Labour
Heath 6 1970 2 1974 Conservative
Wilson 3 1974 3 1976 Labour
Callaghan 4 1976 4 1979 Labour
Thatcher 5 1979 11 1990 Conservative
Major 12 1990 4 1997 Conservative
Blair 5 1997 6 2007 Labour
Brown 6 2007 5 2010 Labour
EDIT: I've just discovered if the size of every variable in Data is increased by one, the code runs without any of the issues. I assume this is some kind of overflow?
EDIT 2: Specifically if EndYr is [13] not [12] the problem is eliminated.
The word Conservative is 12 characters, but you must account for the null char '\0' at the end of every C string.
That is why your code works when you use 13 chars array for the Party field.
What you should do
Specify the maximum length of the Party field in the scanf format specifier. For example, if you keep 12 chars array for the party field:
fscanf(PriMins, "%s %d %d %d %d %11s", &PMs.Name[Max], &PMs.StrtMnth[Max], &PMs.StrtYr[Max], &PMs.EndMnth[Max], &PMs.EndYr[Max], &PMs.Party[Max])
You are reading 13 records from the file and have space to store only 12 of them as all you data items are of size 12.If you increase the size by 1 there is enough room for all 13 records
struct Data
{
char Name[13][15];
int StrtMnth[13];
int StrtYr[13];
int EndMnth[13];
int EndYr[13];
char Party[13][15];
int TimePwr[13];
};

C Primer 5th - Task 14-6

A text file holds information about a softball team. Each line has data arranged as follows:
4 Jessie Joybat 5 2 1 1
The first item is the player's number, conveniently in the range 0–18. The second item is the player's first name, and the third is the player's last name. Each name is a single word. The next item is the player's official times at bat, followed by the number of hits, walks, and runs batted in (RBIs). The file may contain data for more than one game, so the same player may have more than one line of data, and there may be data for other players between those lines. Write a program that stores the data into an array of structures. The structure should have members to represent the first and last names, the at bats, hits, walks, and RBIs (runs batted in), and the batting average (to be calculated later). You can use the player number as an array index. The program should read to end-of-file, and it should keep cumulative totals for each player.
The world of baseball statistics is an involved one. For example, a walk or reaching base on an error doesn't count as an at-bat but could possibly produce an RBI. But all this program has to do is read and process the data file, as described next, without worrying about how realistic the data is.
The simplest way for the program to proceed is to initialize the structure contents to zeros, read the file data into temporary variables, and then add them to the contents of the corresponding structure. After the program has finished reading the file, it should then calculate the batting average for each player and store it in the corresponding structure member. The batting average is calculated by dividing the cumulative number of hits for a player by the cumulative number of at-bats; it should be a floating-point calculation. The program should then display the cumulative data for each player along with a line showing the combined statistics for the entire team.
team.txt (text file I'm working with):
4 Jessie Joybat 5 2 1 1
4 Jessie Joybat 7 3 5 3
7 Jack Donner 6 3 1 2
11 Martin Garder 4 3 2 1
15 Jaime Curtis 7 4 1 2
2 Curtis Michel 3 2 2 3
9 Gillan Morthim 9 6 6 7
12 Brett Tyler 8 7 4 3
8 Hans Gunner 7 7 2 3
14 Jessie James 11 2 3 4
12 Brett Tyler 4 3 1 3
Since I'm a beginner in C, either I misinterpreted the task from what was asked originally or it's unfairly complex (I believe the former is the case). I'm so lost that I can't think of the way how could I fill in by the criteria of index (player number) every piece of data, keep track of whether he has more than one game, calculate and fetch bat average and then print.
What I have so far is:
#define LGT 30
struct profile {
int pl_num;
char name[LGT];
char lname[LGT];
int atbat[LGT/3];
int hits[LGT/3];
int walks[LGT/3];
int runs[LGT/3];
float batavg;
};
//It's wrong obviously but it's a starting point
int main(void)
{
FILE *flx;
int i,jc,flow=0;
struct profile stat[LGT]={{0}};
if((flx=fopen("team.txt","r"))==NULL) {
fprintf(stderr,"Can't read file team!\n");
exit(1);
}
for( jc = 0; jc < 11; jc++) {
fscanf(flx,"%d",&i);
stat[i].pl_num=i;
fscanf(flx,"%s",&stat[i].name);
fscanf(flx,"%s",&stat[i].lname);
fscanf(flx,"%d",&stat[i].atbat[flow]);
fscanf(flx,"%d",&stat[i].hits[flow]);
fscanf(flx,"%d",&stat[i].walks[flow]);
fscanf(flx,"%d",&stat[i].runs[flow]);
flow++;
}
}
Advice 1: don't declare arrays like atbat[LGT/3].
Advice 2: Instead of multiple fscanf you could read the whole line in a shot.
Advice 3: Since the number of players is limited and the player number has a good range (0-18), using that player number as an index into the struct array is a good idea.
Advice 4: Since you need cumulative data for each player (no need to store his history points), then you don't need arrays of integers, just an integer to represent the total.
So:
#include <stdio.h>
#define PLAYERS_NO 19
typedef struct
{
char name[20+1];
char lastName[25+1];
int atbat;
int hits;
int walks;
int runs;
float batavg;
} Profile;
int main(int argc, char** argv)
{
Profile stats[PLAYERS_NO];
int i;
FILE* dataFile;
int playerNo;
Profile tmpProfile;
int games = 0;
for(i=0; i<PLAYERS_NO; ++i)
{
stats[i].name[0] = '\0';
stats[i].lastName[0] = '\0';
stats[i].atbat = 0;
stats[i].hits = 0;
stats[i].walks = 0;
stats[i].runs = 0;
}
dataFile = fopen("team.txt", "r");
if ( dataFile == NULL )
{
fprintf(stderr, "Can't read file team!\n");
exit(1);
}
for(i=0; i<PLAYERS_NO && !feof(dataFile); ++i, ++games)
{
fscanf(dataFile, "%d", &playerNo);
if ( playerNo <0 || playerNo > PLAYERS_NO )
{
fprintf(stderr, "Player number out of range\n");
continue;
}
fscanf(dataFile, "%s %s %d %d %d %d",
&tmpProfile.name,
&tmpProfile.lastName,
&tmpProfile.atbat,
&tmpProfile.hits,
&tmpProfile.walks,
&tmpProfile.runs);
printf("READ: %d %s %s %d %d %d %d\n",
playerNo,
tmpProfile.name,
tmpProfile.lastName,
tmpProfile.atbat,
tmpProfile.hits,
tmpProfile.walks,
tmpProfile.runs);
strcpy(stats[playerNo].name, tmpProfile.name);
strcpy(stats[playerNo].lastName, tmpProfile.lastName);
stats[playerNo].atbat += tmpProfile.atbat;
stats[playerNo].hits += tmpProfile.hits;
stats[playerNo].walks += tmpProfile.walks;
stats[playerNo].runs += tmpProfile.runs;
}
/* exercise: compute the average */
fclose(dataFile);
for(i=0; i<PLAYERS_NO; ++i)
{
if ( stats[i].name[0] == '\0' )
continue;
printf("%d %s %s %d %d %d %d\n",
i,
stats[i].name,
stats[i].lastName,
stats[i].atbat,
stats[i].hits,
stats[i].walks,
stats[i].runs);
}
return 0;
}
The first rule of programming: Divide and conquer.
So you need to identify individual operations. One such operation is "load one row of input", another is "look up a player". If you have some of those operations (more will come up as you go), you can start building your program:
while( more_input ) {
row = load_one_row()
player = find_player( row.name )
if( !player ) {
player = create_player( row.name )
add_player( player )
}
... do something with row and player ...
}
when you have that, you can start to write all the functions.
An important point here is to write test cases. Start with a simple input and test the code to read a row. Do you get the correct results?
If so, test the code to find/create players.
The test cases make sure that you can forget about code that already works.
Use a framework like Check for this.
If I were doing this, I'd start with a structure that only held one "set" of data, then create an array of those structs:
struct profile {
char name[NAMELEN];
char lname[NAMELEN];
int atbat;
int hits;
int walks;
int runs;
float batavg;
};
Since you're using the player's number as the index into an array, you don't need to store it into the structure too.
I think that will simplify the problem a little bit. You don't need to store multiple data items for a single player -- when you get a duplicate, you just ignore some of the new data (like the names, which should be identical) and sum up the others (e.g., at-bats, hits).

Resources