Related
I have a file input.dat. In this file, there are 3 lines:
1 2 3
5 7 10 12
8 9 14 13 15 17
I am going to read one of the three lines using C, and return the number of the elements.
For example, I want to read the 2nd line 5 7 10 12 into memory, and also return the number of values in the 2nd line, which is 4. My code is below...
#include <stdio.h>
#include <stdlib.h>
#define STRING_SIZE 2000
int main() {
FILE *fp = fopen("in.dat", "r");
char line[STRING_SIZE];
int lcount = 0, nline = 1, sum = 0, number;
if (fp != NULL) {
while (fgets(line, STRING_SIZE, fp) != NULL) {
if (lcount == nline) {
while (sscanf(line, "%d ", &number)) {
sum++;
}
break;
} else {
lcount++;
}
}
fclose(fp);
}
exit(0);
}
When I run this code, it never stops like a dead loop. What is the problem here?
the loop while (sscanf(line, "%d ", &number)) keeps parsing the first number in the line.
You should use strtol instead:
#include <stdio.h>
#include <stdlib.h>
#define STRING_SIZE 2000
int main() {
FILE *fp = fopen("in.dat", "r");
char line[STRING_SIZE];
int lcount = 0, nline = 1;
if (fp != NULL) {
while (fgets(line, STRING_SIZE, fp) != NULL) {
if (lcount == nline) {
char *p = line, *q;
int count = 0;
for (;;) {
long val = strtol(p, &q, 0); // parse an integer
if (q == p) {
// end of string or not a number
break;
}
// value was read into val. You can use it for whatever purpose
count++;
p = q;
}
printf("%d\n", count);
break;
} else {
lcount++;
}
}
fclose(fp);
}
return 0;
}
You were thinking along the right path with your use of sscanf(), the only piece of the puzzle you were missing is how to apply an offset to line so that you read the next value in the line with your next call to sscanf(). You do that by keeping track of the number of characters consumed on each call to sscanf() using the "%n" conversion (it does not add to your conversion count returned by sscanf()) For example reading lines from the open file-stream fp, you could do:
#define MAXC 1024 /* if you need a constant, #define one (or more) */
...
char line[MAXC] = ""; /* buffer to hold each line */
...
while (fgets (line, MAXC, fp)) { /* reach each line in file */
int offset = 0, /* offset in line for next sscanf() read */
nchr = 0, /* number of char consumed by last read */
val, /* integer value read with sscanf() */
nval = 0; /* number of values read in line */
/* conververt each integer at line + offset, saving no. of chars consumed */
while (sscanf (line + offset, "%d%n", &val, &nchr) == 1) {
printf (" %d", val); /* output value read */
offset += nchr; /* update offset with no. chars consumend */
nval++; /* increment value count */
}
printf (" - %d values\n", nval); /* output no. values in line */
}
(Note: strtol() provides better error reporting than sscanf() on a failed conversion)
If you put it together with an example that reads from the filename provided as the first argument to the program (or reads from stdin by default if no argument is given), you could do:
#include <stdio.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
int main (int argc, char **argv) {
char line[MAXC] = ""; /* buffer to hold each line */
/* 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 (fgets (line, MAXC, fp)) { /* reach each line in file */
int offset = 0, /* offset in line for next sscanf() read */
nchr = 0, /* number of char consumed by last read */
val, /* integer value read with sscanf() */
nval = 0; /* number of values read in line */
/* conververt each integer at line + offset, saving no. of chars consumed */
while (sscanf (line + offset, "%d%n", &val, &nchr) == 1) {
printf (" %d", val); /* output value read */
offset += nchr; /* update offset with no. chars consumend */
nval++; /* increment value count */
}
printf (" - %d values\n", nval); /* output no. values in line */
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
}
Example Use/Output
With the data you show in the filename dat/nvals.txt you would get:
$ ./bin/fgetsnvals dat/nvals.txt
1 2 3 - 3 values
5 7 10 12 - 4 values
8 9 14 13 15 17 - 6 values
Look things over and let me know if you have further questions.
A bit cleaner version of chqrlie's answer. Started with a string as that is what the question is really about after fgets().
sscanf() won't step through the string, it always reads from the beginning.
strtol() looks for a long int in the beginning of the string, ignoring initial white-space. Gives back the address of where it stops scanning.
The manual of strtol() says that errno should be checked for any conversion error.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#define STRING_SIZE 2000
int main(void)
{
char line[STRING_SIZE] = "5 7 10 12";
char* start = line;
char* end;
int count = 0;
while(1)
{
/**
* strtol() look for long int in beginning of the string
* Ignores beginning whitespace
*
* start: where to strtol() start looking for long int
* end: where strtol() stops scanning for long int
*/
errno = 0; // As strol() manual says
strtol(start, &end, 0);
if (errno != 0)
{
printf("Error in strtol() conversion.\n");
exit(0);
}
if (start == end) break; // Quit loop
start = end;
count++;
}
printf("count: %d\n", count);
return 0;
}
My .txt looks like this for example:
1.0 2.0
1.0 1.0
2.0 3.0
2.0 1.0
I want to run through this file using each line when its needed. To explain I have an equation (L = Ax + B) which I need to calculate for different x's. Is there a way to read each line in the file seperately, taking A and B at that line. So the first term of L will use the first A and B.
I haven't done this before. This is what I know already:
void coefficients(double *A, double *B) {
FILE *infile;
if(2!=fscanf(infile,"%lf %lf",A, B)) {
printf("Error reading parameters from file\n");
exit(1);
}
fclose(infile);
}
And then calling this in int_main:
double A;
double B;
// Read in from file;
coefficients(&A, &B);
You can use this method to read a text file(file.txt) to a char array (buffer) line by line by using fgets() then parse it with sscanf() to find double value in it.
Code
double a=0;
double b=0;
int bufferLength = 1024;
char buffer[bufferLength];
FILE *fp = fopen("file.txt", "r");
if (!fp){
printf("Cant open file\n");
return -1;
}
while(fgets(buffer, bufferLength, fp)) {
//printf("%s\n", buffer);
if (2==sscanf(buffer, "%lf %lf", &a,&b)){
printf("a: %f b: %f\n", a,b);
}
}
fclose(fp);
With a file with potential blank lines and potential lines that contain something other than the pair of double values you are looking for, you are much better off reading each line into a buffer and the parsing the wanted values from the buffer with sscanf rather than attempting to read directly with fscanf. This way a line of input is consumed on each read and you don't have to worry about what remains in your input stream depending on the conversion specifiers used.
Further, in order to read multiple values into A and B, both A and B must be arrays of double (or an allocated block of memory that can hold multiple double values)
For example in main() can declare:
#include <stdio.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
...
int main (int argc, char **argv) {
double a[MAXC] = {0}, b[MAXC] = {0}; /* arrays of MAXC doubles */
Now you have storage for 1024 double values. Your coefficient function that reads the double values should return the number of values read. It should also take the open and validated FILE* stream as a parameter. (if the file cannot be opened in validated in the caller -- there is no reason to make the function call to attempt the read to begin with)
Putting that together you could do:
size_t coefficients (FILE *fp, double *a, double *b)
{
char buf[MAXC]; /* buffer for reading each line */
size_t ncoeff = 0; /* number of coefficient pairs read */
while (ncoeff < MAXC && fgets (buf, MAXC, fp)) /* read each line */
/* if it contains 2 double values */
if (sscanf (buf, "%lf %lf", &a[ncoeff], &b[ncoeff]) == 2)
ncoeff++; /* increment counter */
return ncoeff; /* return total count of pairs read */
}
A short example putting it altogether, you would have:
#include <stdio.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
size_t coefficients (FILE *fp, double *a, double *b)
{
char buf[MAXC]; /* buffer for reading each line */
size_t ncoeff = 0; /* number of coefficient pairs read */
while (ncoeff < MAXC && fgets (buf, MAXC, fp)) /* read each line */
/* if it contains 2 double values */
if (sscanf (buf, "%lf %lf", &a[ncoeff], &b[ncoeff]) == 2)
ncoeff++; /* increment counter */
return ncoeff; /* return total count of pairs read */
}
int main (int argc, char **argv) {
double a[MAXC] = {0}, b[MAXC] = {0}; /* arrays of MAXC doubles */
size_t n = 0; /* count of doubles returned */
/* 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;
}
if (!(n = coefficients (fp, a, b))) { /* validate coeff pairs read */
fputs ("error: no double values read from file.\n", stderr);
return 1;
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
for (size_t i = 0; i < n; i++) /* output coefficients */
printf ("a[%zu] : %.1f b[%zu] : %.1f\n", i, a[i], i, b[i]);
}
(you can adjust the size of the arrays as needed)
Example Input File
$ cat dat/coeffpairs.txt
A B
1.0 2.0
1.0 1.0
2.0 3.0
2.0 1.0
Example Use/Output
$ ./bin/readcoeff dat/coeffpairs.txt
a[0] : 1.0 b[0] : 2.0
a[1] : 1.0 b[1] : 1.0
a[2] : 2.0 b[2] : 3.0
a[3] : 2.0 b[3] : 1.0
Look things over and let me know if you have further questions.
It depends on what you want to do. If you just want to read the data and don't care what happens when it is not in the form of two columns, you can just read it with fscanf in a loop:
double A, B;
while(fscanf(fp, "%ld%ld", &A, &B) == 2) {
// do something with A and B
This will read any sequence of numbers in the file -- maybe in two columns or all on one line or randomly split between lines with blank lines, and will stop when it gets to anything that is not a number (or the end of the file, and will ignore any odd single number at the end).
If on the other hand you want to verify that the numbers are two per line and no blank lines, you can use fgets+sscanf:
char line[256];
while (fgets(line, sizeof line, fp)) {
double A, B;
int end;
if (sscanf(line, "%ld%ld %n", &A, &B, &len) == 2 && line[end] == 0) {
// got a line with (just) a pair of numbers
} else {
fprintf(stderr, "malformed input line: %s", line);
}
}
If you also want to check that the numbers are lined up in columns, that is a bit trickier. You can check for left- or right-aligned columns pretty easily
char line[256];
int a_col = -1, b_col = -1, lineno = 0;
while (fgets(line, sizeof line, fp)) {
double A, B;
int a_right, b_right, end;
++lineno;
if (sscanf(line, "%ld%n%ld%n %n", &A, &a_right, &B, &b_right, &len) == 2 && line[end] == 0) {
// got a line with (just) a pair of numbers
if (a_col >= 0 && (a_col != a_right || b_col != b_right)) {
fprintf(stderr, "columns on line %d not lined up with previous line\n", lineno);
}
a_col = a_right;
b_col = b_right;
} else {
fprintf(stderr, "malformed input on line %d", lineno);
}
}
The Text File was writable with 1 blank line after the struct.
Is similar to:
1 111 1 Peter
22 22 2 John Lays
3 3 3 Anne Belgs
The struct is:
struct estruturaCarro {
int id, potencia, avariado;
char name[11];
} carro;
I have write the data to the text File:
fprintf(fp, "%-2d %-3d %-1d %-10s \n\n", carro.id, carro.potencia, carro.avariado, carro.name);
I read the file data this way, but I'm sure it's not the best way:
while(true){
int result = fscanf(fp, "%d %d %d %10[^\n]", &carro.id, &carro.potencia, &carro.avariado, &carro.name);
if(result==4){
printf("%-2d %-3d %-1d % -s\n", carro.id, carro.potencia, carro.avariado, carro.name);
}else break;
}
How can I read the text file, saving the data struct, without reading the blank lines?
If I want to validate the values read (ID = xx, potencia = xxx, avariado = x, name = 10 characters), is it better, before or after filling the array of struct?The format of each line of the file is:xx xxx x aaaaaaaaaa (x = digits, a = characters)For example, if one of the lines is
9 123 4 Error 1st value
stop reading the file (informing the user), because the line should be:
9 123 4 Error 1st value (one space was missing after 9) - or because NAME has more than 10 characters.
You don't show how true is set in while(true), but it must be based on the return of fscanf in the way you are approaching it. You must test three cases:
fscanf return is EOF, read done, true should be set to FALSE to break loop;
fscanf return less than 4, matching or input failure, no guarantee this occurred due to an empty line; and
fscanf return is equal to 4, good input.
Unless you are checking all three, you cannot cover all cases for fscanf and further, if there is any additional or extraneous character in your input file, your formatted read will fail.
That is why a better option is to read each line into a buffer with fgets and then parse the information you need from the buffer itself with sscanf. This provides the benefit of allowing separate validation of (1) the read; and (2) the parse of information. Further, since you are consuming a line-at-a-time, there is no uncertainty on what remains in the input stream and a failure in parsing any one line does not prevent a successful read of the remainder.
A short implementation with fgets() and sscanf() reading each struct into an array-of-struct, you can do something similar to the following:
#include <stdio.h>
#define MAXN 11 /* if you need a constant, #define one (or more) */
#define MAXC 1024 /* (don't skimp on buffer size) */
typedef struct { /* use a simple typedef */
int id, potencia, avariado;
char name[MAXN];
} carro_t;
int main (int argc, char **argv) {
char buf[MAXC]; /* buffer to read line */
carro_t carro[MAXN] = {{ .id = 0 }}; /* array of struct */
size_t ndx = 0; /* array index */
/* 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 (ndx < MAXN && fgets (buf, MAXC, fp)) { /* read each line */
carro_t tmp = { .id = 0 }; /* temp struct */
if (*buf == '\n') /* if line empty */
continue; /* get next line */
if (sscanf (buf, "%d %d %d %10[^\n]", /* separate/validate */
&tmp.id, &tmp.potencia, &tmp.avariado, tmp.name) == 4)
carro[ndx++] = tmp; /* add to array of struct */
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (size_t i = 0; i < ndx; i++)
printf ("%3d %3d %3d %s\n",
carro[i].id, carro[i].potencia, carro[i].avariado, carro[i].name);
return 0;
}
(note: the filename is provide as the first argument to the program, or if no filename is provided, read from stdin by default)
While with your particular data file, there is no reason you cannot use fscanf, it is fragile and one character too many (like "Anne Belgss") will cause it to break. A fscanf implementation removing the true and simply looping as you have could be:
for (;;) {
carro_t tmp = { .id = 0 };
if (fscanf (fp, "%d %d %d %10[^\n]", /* separate/validate */
&tmp.id, &tmp.potencia, &tmp.avariado, tmp.name) == 4)
carro[ndx++] = tmp; /* add to array of struct */
else
break;
}
Either way, you will produce the same output with "this" input file, e.g.
Example Use/Output
$ ./bin/readwblanks ~/tmpd/file
1 111 1 Peter
22 22 2 John Lays
3 3 3 Anne Belgs
just use simple reading way is ok,like this.
while(fscanf(fp,"%d %d %d %s",&carro.id,&carro.potencia,&carro.avariado,carro.name)!=EOF)
printf("%d %d %d %s\n",carro.id,carro.potencia,carro.avariado,carro.name);
result is like this
enter image description here
I'm writing a program that needs to read a CSV file and check that all letters in the alphabet appear one time on each side of the comma. The file would look something like this:
a,x
b,j
c,g
d,l
e,s
f,r
g,u
h,z
i,w
j,c
k,e
l,a
m,v
but there would be 26 lines total. What would be the most efficient way to check that each side has all 26 letters with no repeats?
While it is unclear from your question and follow-up comments where exactly you are stuck, or whether you have thrown in the towel and given up, let's take it from the beginning.
Open Your File (or reading stdin)
Before you can do anything with the content of your file, you need to open you file for reading. For reading formatted-input you will generally use the functions that read and write from a file stream using a FILE * stream pointer (as opposed to the low-level file-descriptor file interface). To open your file, you will call fopen and check the return to validate the open succeeded.
Do not hard-code filenames or numbers in your program. Your program takes arguments, either pass the filename to open as an argument, or prompt for entry of the filename. You can increase the flexibility of your program by taking the filename to read as an argument, or read from stdin by default if no argument is provided (as most Linux utilities do). Since stdin is a file stream, you can simply assign it to your FILE* pointer if you are not opening a filename provided as an argument. For example:
FILE *fp = NULL;
if (argc > 1) /* if one argument provided */
fopen (argv[1], "r"); /* open file with name from argument */
else
fp = stdin; /* set fp to stdin */
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
which can be shortened using the ternary operator, e.g.:
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
Reading Your Data
With a file-stream open and validated, you can now read your data from your file. While you could read with fscanf, you are limited in the information it provides in the event less that two values are read. Additionally, reading with the scanf family of functions is full of pitfalls due to what characters remain in your input file-stream depending on the conversion specifiers used and on whether the conversion succeeded or failed. Nonetheless, a simple approach that validates two conversions took place according to your format-string will allow you to read your file, e.g.
char c1, c2; /* characters from each line */
int freq1[MAXC] = {0}, freq2[MAXC] = {0}; /* frequency arrays */
...
while (fscanf (fp, " %c, %c", &c1, &c2) == 2) /* read all chars */
if (c1 > 0 || c2 > 0) /* validate ASCII values */
/* increment element in each */
freq1[(unsigned char)c1]++, freq2[(unsigned char)c2]++;
(the downside is any variation in format on one line can leave you with unwanted characters stored, and if less than two conversions take place, your read loop stops despite valid data remaining unread)
A better approach is to read a line-at-a-time with a line-oriented input function such as fgets or POSIX getline. With this approach, you are consuming a line of data at a time, and then parsing the needed information from the stored line. The benefits are significant. You have an independent validation of the read itself, and then whether you find the needed values in the line. If your format varies an you parse less than the needed values from the line, you have the option of simply skipping that line and continuing with the next. Further, what remains in your input file stream does not depend on the conversion specifiers used.
An example with fgets and sscanf doing the same thing would be:
char c1, c2, /* characters from each line */
buf[MAXC] = ""; /* buffer to hold each line */
...
while (fgets (buf, MAXC, fp)) /* read all chars */
if (sscanf (buf, " %c, %c", &c1, &c2) == 2) { /* parse values */
if (c1 > 0 || c2 > 0) /* validate ASCII values */
/* increment element in each */
freq1[(unsigned char)c1]++, freq2[(unsigned char)c2]++;
}
else
fputs ("error: in line format.\n", stderr);
Handling the Frequency of Characters
If you have been paying attention to the read of the data from the file, you will note that a pair of frequency arrays have been incremented on each read of the characters freq1 and freq2. As mentioned in my comments above, you start with an adequately sized array of int to hold the ASCII character set. The arrays are initialized to zero. When you read a character from each column, you simply increment the value at:
if (c1 > 0 || c2 > 0) /* validate ASCII values */
/* increment each element */
freq1[(unsigned char)c1]++, freq2[(unsigned char)c2]++;
For example the ASCII value for 'a' is 97 (see ASCII Table and Description). So if you read an 'a' and increment
freq1['a']++;
that is the same as incrementing:
freq1[97]++;
When you are done with your read loop, you simply need to iterate over your frequency arrays from 'a' to 'z' and the number of times the corresponding character appeared in your file will be captured in your array. Then you can use the data however you like.
Outputting The Results
The simplest way to output your column1/column2 results is simply to output the number of occurrences for each character. For example:
for (int i = 'a'; i <= 'z'; i++) /* loop over 'a' to 'z' */
printf (" %c: %d, %d\n", i, freq1[i], freq2[i]);
Which will produce output similar to:
$ ./bin/freq_dual_col2 <dat/char2col.txt
lowercase occurrence:
a: 1, 1
b: 1, 0
c: 1, 1
d: 1, 0
e: 1, 1
f: 1, 0
...
If you wanted to get a little more verbose and note whether the characters appears "none", or 1 or whether the character was duplicated "dupe", you could employ a few additional checks, e.g.
for (int i = 'a'; i <= 'z'; i++) { /* loop over 'a' to 'z' */
if (freq1[i] == 1) /* check col 1 chars */
printf (" %c , ", i);
else if (!freq1[i])
fputs ("none, ", stdout);
else
fputs ("dupe, ", stdout);
if (freq2[i] == 1) /* check col 2 chars */
printf (" %c\n", i);
else if (!freq2[i])
fputs ("none\n", stdout);
else
fputs ("dupe\n", stdout);
}
Which would produce output as:
$ ./bin/freq_single_dual_col <dat/char2col.txt
lowercase single occurrence, none or dupe:
a , a
b , none
c , c
d , none
e , e
f , none
...
Putting it altogether, your minimal example using fscanf for your read could be similar to:
#include <stdio.h>
#include <limits.h>
#define MAXC UCHAR_MAX+1
int main (int argc, char **argv) {
char c1, c2; /* characters from each line */
int freq1[MAXC] = {0}, freq2[MAXC] = {0}; /* frequency arrays */
/* 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 (fscanf (fp, " %c,%c", &c1, &c2) == 2) /* read all chars */
if (c1 > 0 || c2 > 0) /* validate ASCII values */
/* increment each element */
freq1[(unsigned char)c1]++, freq2[(unsigned char)c2]++;
if (fp != stdin) fclose (fp); /* close file if not stdin */
puts ("lowercase occurrence:\n");
for (int i = 'a'; i <= 'z'; i++) /* loop over 'a' to 'z' */
printf (" %c: %d, %d\n", i, freq1[i], freq2[i]);
return 0;
}
The example using fgets and sscanf would be similar to:
#include <stdio.h>
#include <limits.h>
#define MAXC UCHAR_MAX+1
int main (int argc, char **argv) {
char c1, c2, /* characters from each line */
buf[MAXC] = ""; /* buffer to hold each line */
int freq1[MAXC] = {0}, freq2[MAXC] = {0}; /* frequency arrays */
/* 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 (fgets (buf, MAXC, fp)) /* read each line */
if (sscanf (buf, " %c, %c", &c1, &c2) == 2) { /* parse values */
if (c1 > 0 || c2 > 0) /* validate ASCII values */
/* increment each element */
freq1[(unsigned char)c1]++, freq2[(unsigned char)c2]++;
}
else
fputs ("error: in line format.\n", stderr);
if (fp != stdin) fclose (fp); /* close file if not stdin */
puts ("lowercase occurrence:\n");
for (int i = 'a'; i <= 'z'; i++) /* loop over 'a' to 'z' */
printf (" %c: %d, %d\n", i, freq1[i], freq2[i]);
return 0;
}
And if you wanted the more verbose output, then I leave it to you to incorporate it in the code above.
Look things over and let me know if your have further questions.
Add all columns to Sets and check if the Sets are same size of file lines.
remember that Sets ignore duplicates
My code pulls data from one text file and then totals the points and enters it into a separate text file, so I would like the program to organize the teams and scores by total points prior to frpintf into the text file. So far the program pulls and runs the data and totals it out and fprintf just fine, should I use qsort to sort and print into the text file, and where in my code do I place it. Here is the text it is reading.
Indians 54 45 5
Twins 45 53 7
Tigers 43 59 8
White_Sox 35 64 9
Royals 30 69 3
Also I know there are no ties in the MLB lol just throwing in an additional variable.
Thanks.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
int main(void)
{
struct records {
char filename;
char team[50];
int wins;
int tie;
int loss;
int points;
};
struct records roster;
FILE *ifp = NULL;
FILE *afd = NULL;
const int argv;
char filename2[64] = {0};
char filename[64] = {0};
int points;
int points2;
int total;
printf("\nPlease enter the the .txt file you would like to open: ");
scanf("%63s", filename);
printf("Opened file name %s",filename);
ifp = fopen(filename,"r");
if (ifp == NULL)
{
printf("Could not open");
printf("\nPlease enter the the .txt file you would like to open:");
scanf("%63s", filename);
printf("Opened file name %s",filename);
ifp = fopen(filename,"r");
}
printf("\nReading the file %s \n", filename);
while(fscanf(ifp, "%s %d %d%d" , roster.team, &roster.wins, &roster.loss,
&roster.tie) != EOF)
printf("%s Wins:%d Losses:%d ties:%d\n", roster.team, roster.wins,
roster.loss, roster.tie);
printf("\nWins are worth 2 points ties are worth 1 point and losses are
worth \nzero in overall league standings.");
printf("\nHere are the overall ALCentral Standings!\n");
ifp = fopen(filename, "r");
fopen(filename, "r"); while(fscanf(ifp, "%s %d %d %d", roster.team,
&roster.wins, &roster.loss, &roster.tie) != EOF)
printf("%s Wins:%d Losses:%d ties:%d Total league points:%d\n",
roster.team, roster.wins, roster.loss, roster.tie, (roster.wins * 2 +
roster.tie * 1), total);
printf("closing %s", filename);
fclose(ifp);
printf("\nPlease enter the the .txt file you would like to write to: ");
scanf("%63s", filename2);
printf("Opened file name %s", filename2);
afd = fopen(filename2, "a+");
if (afd == NULL)
{
printf("Could not open"); printf("\nPlease enter the the .txt file you
would like to open: ");
scanf("%63s", filename2);
printf("Opened file name %s", filename2);
afd = fopen(filename2,"a+");
}
ifp = fopen(filename,"r");
fopen(filename, "r");
points = roster.wins * 2;
points2 = roster.tie * 1;
total = points + points2;
while(fscanf(ifp, "%s %d %d %d", roster.team, &roster.wins, &roster.loss,
&roster.tie) != EOF)
fprintf(afd, "%s Wins:%d Losses:%d ties:%d total league points:%d\n",
roster.team, roster.wins, roster.loss, roster.tie, (roster.wins *2 +
roster.tie *1 ), total, filename2);
printf("\nYour stats have been recorded to the selected
file!\nClosing all open files! \nThanks for using the STAT-Master!
Have a great day!
fclose(afd);
fclose(ifp);
return 0;
}
Your logic is quite jumbled. If I understand what you are attempting, you want to read each teams wins, loss and tie record from your data file, and then compute the points that are then used to sort the teams into descending (or perhaps ascending) order and then output the "the overall ALCentral Standings" to either stdout or a file, or both.
Before starting let's talk about your definition of struct records inside of main(). While legal, you generally will want your struct declaration to be at file scope so that the struct type is available to any functions you write to help process the data in your code. Here, you must move the declaration outside of main() because the qsort compare function will need to know what the type struct records is. The compare function cannot be declared and defined in main() because C does not allow nested function declarations. So move your struct outside of main(), e.g.
#define MAXR 16 /* if you need a constant, #define one (or more) */
#define MAXC 64
#define MAXB 1024
typedef struct records {
char team[MAXC];
int wins,
loss,
tie,
points;
} record_t;
(note: using a typedef can make things more convenient)
To begin with, your multiple attempts to read the filename is awkward and never insures you actually have a file open. It just makes a couple of attempts and then effectively gives up validation and just blindly assumes there is a valid file stream to read and write to and presses ahead. This is a recipe for invoking Undefined Behavior.
When ever you do anything in C, and especially when you are dealing with user input or file operations, you must validate every step. You must know whether or not the file is open for reading before you attempt to read. (no matter how many times you attempt to open it) You must validate what you have read before you begin using the values in your code. You must validate the state of your input stream before you attempt your next read, and handle any characters that remain.
With that in mind you can prompt for the filename and attempt to open the filename provided in a continual loop until you validate the file is actually open, e.g.
/** helper function to empty stdin between inputs */
void empty_stdin()
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
...
FILE *fp = NULL; /* file pointer */
do { /* loop until valid filename given and file open */
printf("\nenter input filename: ");
if (scanf (" %63[^\n]", filename) != 1) {
fprintf (stderr, "error: user canceled input.\n");
return 1;
}
empty_stdin(); /* remove '\n' and any add'l chars in input buffer */
fp = fopen (filename, "r"); /* open filename */
if (fp == NULL) /* validate file open for reading */
perror ("fopen-input");
} while (fp == NULL);
printf("Opened file name %s\n\n",filename);
(note: rather than using scanf for user input, fgets provides a better approach to user input and far fewer pitfalls that scanf, example below with roster)
Next, your understanding of how to collect the data in memory in order to sort it, is not evident from your post. When you want to sort a collection of team information, you must read all team information into memory before you can pass the information to qsort for sorting.
You have a struct that holds all of information for each team, you simply need to read each line (record) from your input file into an array or struct which can then easily be passed to qsort for sorting.
To use qsort you first must define a compare function that is used to qsort to order your data. The compare function has a specific declaration:
int compare (const void *a, const void *b);
Where each const void * parameter will be a pointer to one of the elements in your array of records. (in C a void pointer, e.g. void* is a generic pointer that can be assigned from, or to, any other pointer type without a cast -- that is why the compare function's declaration uses that type for the parameters) Your job in writing a compare function is simply to provide a type to the pointers (either by assignment or by explicit cast, your choice) and then a comparison between the values that you wish to sort by. For example, your compare on the points member of each struct could simply be:
/** qsort compare function (descending order of points) */
int compare (const void *a, const void *b)
{
const record_t *ta = a;
const record_t *tb = b;
return (ta->points < tb->points) - (ta->points > tb->points);
}
(where the (a < b) - (a > b) simply evaluates to -1, 0, 1 depending on the inequalites, e.g. if a = 4 and b = 3, you have (0) - (1) which equals -1 meaning a sorts before b)
All that remains is reading each teams data into your array and computing points and then passing your array to qsort.
To read your data into an array, read each line into a buffer, parse the data in the buffer into the individual values, validate that each of the individual values where parsed correctly, compute the points value for each team, increment your array index and then go read the next line. You could do something like the following using fgets to read each line of data:
record_t roster[MAXR] = {{ .team = "" }}; /* array to hold teams */
...
/* read up to MAXR team records from file */
while (ndx < MAXR && fgets (buf, MAXB, fp)) {
/* parse data from buffer filled, saving return */
int rtn = sscanf (buf, " %49s %d %d %d" , roster[ndx].team,
&roster[ndx].wins, &roster[ndx].loss,
&roster[ndx].tie);
if (rtn < 4) /* if less than 4 conversions, get next line */
continue;
/* compute points (2 * wins + ties) */
roster[ndx].points = roster[ndx].wins * 2 + roster[ndx].tie;
ndx++; /* increment index */
}
fclose (fp); /* close file */
(note: you can add a strlen() check of buf to determine if additional characters remain in the line unread -- that is left to you).
With all you data in your array of structs roster, all that remains is passing roster to qsort and then outputting your sorted data.
The following sorts the data and outputs the results to stdout. Writing the output to a file is left to you, but hint, you don't need both ifp and afp and you don't need filename and filename2 or points2, you are only dealing with one file/filename at a time. The sort and output is simple:
/* sort roster based on points (descending order) */
qsort (roster, ndx, sizeof *roster, compare);
/* outpout results */
printf ("Here are the overall ALCentral Standings!\n\n"
"Team Wins Loss Tie Points\n\n");
for (int i = 0; i < ndx; i++)
printf ("%-12s %4d %4d %4d %4d\n", roster[i].team,
roster[i].wins, roster[i].loss, roster[i].tie,
roster[i].points);
Putting it altogether in an example, you could do something like the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXR 16 /* if you need a constant, #define one (or more) */
#define MAXC 64
#define MAXB 1024
typedef struct records {
char team[MAXC];
int wins,
loss,
tie,
points;
} record_t;
/** helper function to empty stdin between inputs */
void empty_stdin()
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
/** qsort compare function (descending order of points) */
int compare (const void *a, const void *b)
{
const record_t *ta = a;
const record_t *tb = b;
return (ta->points < tb->points) - (ta->points > tb->points);
}
int main (void) {
int ndx = 0; /* index for roster array */
char buf[MAXB] = "", /* buffer for fgetrs */
filename[MAXC] = ""; /* filename to read */
record_t roster[MAXR] = {{ .team = "" }}; /* array to hold teams */
FILE *fp = NULL; /* file pointer */
do { /* loop until valid filename given and file open */
printf("\nenter input filename: ");
if (scanf (" %63[^\n]", filename) != 1) {
fprintf (stderr, "error: user canceled input.\n");
return 1;
}
empty_stdin(); /* remove '\n' and any add'l chars in input buffer */
fp = fopen (filename, "r"); /* open filename */
if (fp == NULL) /* validate file open for reading */
perror ("fopen-input");
} while (fp == NULL);
printf("Opened file name %s\n\n",filename);
/* read up to MAXR team records from file */
while (ndx < MAXR && fgets (buf, MAXB, fp)) {
/* parse data from buffer filled, saving return */
int rtn = sscanf (buf, " %49s %d %d %d" , roster[ndx].team,
&roster[ndx].wins, &roster[ndx].loss,
&roster[ndx].tie);
if (rtn < 4) /* if less than 4 conversions, get next line */
continue;
/* compute points (2 * wins + ties) */
roster[ndx].points = roster[ndx].wins * 2 + roster[ndx].tie;
ndx++; /* increment index */
}
fclose (fp); /* close file */
/* sort roster based on points (descending order) */
qsort (roster, ndx, sizeof *roster, compare);
/* outpout results */
printf ("Here are the overall ALCentral Standings!\n\n"
"Team Wins Loss Tie Points\n\n");
for (int i = 0; i < ndx; i++)
printf ("%-12s %4d %4d %4d %4d\n", roster[i].team,
roster[i].wins, roster[i].loss, roster[i].tie,
roster[i].points);
return 0;
}
Example Input File
The file is provided as given, with blank lines in between records.
$ cat dat/teams.txt
Indians 54 45 5
Twins 45 53 7
Tigers 43 59 8
White_Sox 35 64 9
Royals 30 69 3
Example Use/Output
$ ./bin/qsortstrcmp
enter input filename: dat/teams.txt
Opened file name dat/teams.txt
Here are the overall ALCentral Standings!
Team Wins Loss Tie Points
Indians 54 45 5 113
Twins 45 53 7 97
Tigers 43 59 8 94
White_Sox 35 64 9 79
Royals 30 69 3 63
Look things over and let me know if you have further questions.