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);
}
}
Related
i am trying to read a text file and extract the cordinates of 'X' so that i can place then on a map
the text file is
10 20
9 8 X
2 3 P
4 5 G
5 6 X
7 8 X
12 13 X
14 15 X
I tried multiple times but I am unable to extract the relevant data and place it in separate variables to plot
I am quite new to c and am trying to learn things so any help is appreciated
thanks in advance
From my top comments, I suggested an array of point structs.
Here is your code refactored to do that.
I changed the scanf to use %s instead of %c for the point name. It generalizes the point name and [probably] works better with the input line because [I think] the %c would not match up correctly.
It compiles but is untested:
#include <stdio.h>
#include <stdlib.h>
struct point {
int x;
int y;
char name[8];
};
struct point *points;
int count;
int map_row;
int map_col;
void
read_data(const char *file_name)
{
FILE *fp = fopen(file_name, "r");
if (fp == NULL) {
/* if the file opened is empty or has any issues, then show the error */
perror("File Error");
return;
}
/* get the dimensions from the file */
fscanf(fp, "%d %d", &map_row, &map_col);
map_row = map_row + 2;
map_col = map_col + 2;
while (1) {
// enlarge dynamic array
++count;
points = realloc(points,sizeof(*points) * count);
// point to place to store data
struct point *cur = &points[count - 1];
if (fscanf(fp, "%d %d %s", &cur->x, &cur->y, cur->name) != 3)
break;
}
// trim to amount used
--count;
points = realloc(points,sizeof(*points) * count);
fclose(fp);
}
There are a number of way to approach this. Craig has some very good points on the convenience of using a struct to coordinate data of different types. This approach reads with fgets() and parses the data you need with sscanf(). The benefit eliminates the risk of a matching-failure leaving characters unread in your input stream that will corrupt the remainder of your read from the point of matching-failure forward. Reading with fgets() you consume a line of input at a time, and that read is independent of the parsing of values with sscanf().
Putting it altogether and allowing the filename to be provided by the first argument to the program (or reading from stdin by default if no argument is provided), you can do:
#include <stdio.h>
#define MAXC 1024 /* if you need a constand, #define one (or more) */
int main (int argc, char **argv) {
char buf[MAXC]; /* buffer to hold each line */
int map_row, map_col; /* map row/col variables */
/* use filename provided as 1st argument (stdin if none provided) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open");
return 1;
}
/* read/validate first line saving into map_row, map_col */
if (!fgets (buf, MAXC, fp) ||
sscanf (buf, "%d %d", &map_row, &map_col) != 2) {
fputs ("error: EOF or invalid map row/col data.\n", stderr);
return 1;
}
/* loop reading remaining lines, for used as line counter */
for (size_t i = 2; fgets (buf, MAXC, fp); i++) {
char suffix;
int x, y;
/* validate parsing x, y, suffix from buf */
if (sscanf (buf, "%d %d %c", &x, &y, &suffix) != 3) {
fprintf (stderr, "error: invalid format line %zu.\n", i);
continue;
}
if (suffix == 'X') { /* check if line suffix is 'X' */
printf ("%2d %2d %c\n", x, y, suffix);
}
}
if (fp != stdin) { /* close file if not stdin */
fclose (fp);
}
}
(note: this just illustrates the read and isolation of the values from lines with a suffix of 'X'. Data handling, and calculations are left to you)
Example Use/Output
With your data in dat/coordinates.txt you could do:
$ ./bin/readcoordinates dat/coordinates.txt
9 8 X
5 6 X
7 8 X
12 13 X
14 15 X
As Craig indicates, if you need to store your matching data, then an array of struct provides a great solution.
I have a text file that has 200 x 150 data in it (every number is separated with a space). I am trying to store these numbers into a matrix. My code is this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int i,j, matrix[200][150];
void getMatrix(){
FILE *file;
file = fopen("PICTURE.txt", "r");
char *line, *number;
j = 0;
while(!feof(file)){
i = 0;
fscanf(file, "%s", line);
number = strtok(NULL, " ");
while(number!= NULL){
matrix[i][j] = atoi(number);
}
printf("\n");
j++;
}
}
void printMatrix(){
int a, b;
for(a=0; a<i; a++){
for(b=0; b<j; b++){
printf("%d", matrix[a][b]);
}
printf("\n");
}
}
int main(){
getMatrix();
printMatrix();
}
It prints out literally nothing. I don't understand why. How can I fix my code or do you have any other suggestions for rewriting it?
if you have a file, with 200x150 integer values (either on 200 lines, or all values on one line), then simply looping though the file with fscanf() reading and storing an integer at a time is all that is needed. You must validate each read and provide a way to communicate any failure within a function through the function return (or though a pointer updated within the function)
To begin, do not use MagicNumbers in your code and do not hardcode filenames. Instead, #define the constants you need (or use a global enum) and pass the open FILE* pointer to your read-function as a parameter after having opened (and validated that the file is open for reading) in the calling function. (If the file open fails -- there is no reason to make the read-function call to begin with)
In your case, you only have two needed constants:
#define ROWS 200 /* if you need a constant, #define one (or more) */
#define COLS 150
Do not use global variables unless absolutely necessary (there will be virtually no case where globals are needed when you begin learning C). Passing the matrix (2D array) as a parameter to your read-function, it can be written as:
/* fill m[ROWS][COLS] with values read from fp.
* returns 1 on success, 0 otherwise.
*/
int getmatrix (int (*m)[COLS], FILE *fp)
{
for (int row = 0; row < ROWS; row++) {
for (int col = 0; col < COLS; col++) {
if (fscanf (fp, "%d", &m[row][col]) != 1) {
fprintf (stderr, "error reading m[%d][%d]\n", row, col);
return 0;
}
}
}
return 1;
}
(note: how the return type for the function is int where 1 (true) is returned on success and 0 (false) is returned on failure. Also note that if 200x150 values are unable to be read, failure is returned)
Pass the filename to read from as the 1st argument to your program (that's what int argc, char **argv parameters to main() are for). Alternatively, you can prompt the user and read the filename as input. You can provide a fixed filename as a default value if no filename is entered (stdin is used as the default below), but otherwise do not hardcode filenames. You should not have to recompile your program simply to read from a different filename.
With those improvements made, your main() could be:
int main (int argc, char **argv) {
int matrix[ROWS][COLS] = {{0}}; /* do not use global variables */
/* 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 in caller */
perror ("file open failed");
return 1;
}
if (!getmatrix (matrix, fp)) { /* pass array and open FILE*, validate */
return 1;
}
fclose (fp);
prnmatrix (matrix); /* output results */
}
The complete program is:
#include <stdio.h>
#define ROWS 200 /* if you need a constant, #define one (or more) */
#define COLS 150
/* fill m[ROWS][COLS] with values read from fp.
* returns 1 on success, 0 otherwise.
*/
int getmatrix (int (*m)[COLS], FILE *fp)
{
for (int row = 0; row < ROWS; row++) {
for (int col = 0; col < COLS; col++) {
if (fscanf (fp, "%d", &m[row][col]) != 1) {
fprintf (stderr, "error reading m[%d][%d]\n", row, col);
return 0;
}
}
}
return 1;
}
void prnmatrix (int (*m)[COLS])
{
for (int row = 0; row < ROWS; row++) {
for (int col = 0; col < COLS; col++) {
printf (col ? " %4d" : "%4d", m[row][col]);
}
putchar ('\n');
}
}
int main (int argc, char **argv) {
int matrix[ROWS][COLS] = {{0}}; /* do not use global variables */
/* 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 in caller */
perror ("file open failed");
return 1;
}
if (!getmatrix (matrix, fp)) { /* pass array and open FILE*, validate */
return 1;
}
fclose (fp);
prnmatrix (matrix); /* output results */
}
Example Use/Output
With 200 x 150 random values less than 10000 generated and written to dat/int-200x150.txt, you would run the program as:
$ ./bin/read2darrint dat/int-200x150.txt
9240 5939 3063 5789 7401 7869 4363 3321 7788 1008 7850 ...
2760 5263 5338 6390 8146 155 324 916 4489 3718 1616 ...
...
Where the output is 200 lines of 150 numbers each.
There are many ways to do this. One (better) way would be to loop reading each line of integer values as a string with fgets() and converting each set of ASCII digits into an integer value with strtol(). I say "better" in a sense that strtol() provides far better error reporting in the case of a failed conversion that the simple pass/fail that can be obtained from fscanf().
The read before looping with strtol() would be with fgets() into a sufficiently sized buffer. That way if a matching-failure occurs, the error only impacts that line of data and no offending data is left in your input stream unread (as will occur with a matching-failure and fscanf().
That said, with a properly formatted space-separated file full of data, there is nothing wrong with using fscanf() directly, just be aware there are more robust ways to do the read and conversion.
To use strtol(), to work through each line of values, you simply use a pointer to the beginning of each set of digits calling strtol() which then updates the endptr to one past the last digit converted allowing you to work your way though each number by updating the pointer (nptr) to endptr after each validated conversion. See man 3 strtol. You could updated the code above with:
...
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
...
#define MAXC 2048 /* (150 int + space) max of 1800 chars per-line */
and finally change your function to use strtol(), e.g.
/* fill m[ROWS][COLS] with values read from fp.
* returns 1 on success, 0 otherwise.
*/
int getmatrix (int (*m)[COLS], FILE *fp)
{
char buf[MAXC]; /* buffer to hold each line */
for (int row = 0; row < ROWS; row++) {
char *nptr = buf, *endptr = nptr; /* nptr and endptr for strtol */
if (!fgets (buf, MAXC, fp)) { /* read line of input */
fprintf (stderr, "error: failed to read line %d\n", row);
return 0;
}
for (int col = 0; col < COLS; col++) {
errno = 0; /* reset errno */
long tmp = strtol (nptr, &endptr, 10); /* convert w/strtol */
if (nptr == endptr) { /* no ASCII digits coverted to a number */
fputs ("error: no digits converted.\n", stderr);
return 0;
}
else if (errno) { /* error in converstion, under/overflow */
fputs ("error: under/overflow occurred in conversion.\n", stderr);
return 0;
}
/* conversion outside range of int */
else if (tmp < INT_MIN || INT_MAX < tmp) {
fputs ("error: conversion out of range of integer.\n", stderr);
return 0;
}
m[row][col] = tmp; /* assign tmp to m[row][col] */
nptr = endptr; /* update nptr to endptr */
}
}
return 1;
}
If an error occurs, you will know exactly what caused the failure. Look things over and let me know if you have further questions.
Many problems:
Never use while(!feof(file)){. Instead check the return value of fscanf(file, ...)
Do not use "%s" with out a width limit.
Do not use pass line to a function without initializing/assigning it first.
i never incremented.
fclose(file) when done.
Check success of fopen(...);
GTG, maybe more later.
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;
}
I have a .txt file that contains data in this format:
xxxx: 0.9467,
yyyy: 0.9489,
zzzz: 0.78973,
hhhh: 0.8874,
yyyy: 0.64351,
xxxx: 0.8743,
and so on...
Let's say that my C program receives, as input, the string yyyy. The program should, simply, return all the instances of yyyy in the .txt file and the average of all their numerical values.
int main() {
FILE *filePTR;
char fileRow[100000];
if (fopen_s(&filePTR, "file.txt", "r") == 0) {
while (fgets(fileRow, sizeof fileRow, filePTR) != NULL) {
if (strstr(fileRow, "yyyy") != NULL) { // Input parameter
printf("%s", fileRow);
}
}
fclose(filePTR);
printf("\nEnd of the file.\n");
} else {
printf("ERROR! Impossible to read the file.");
}
return 0;
}
This is my code right now. I don't know how to:
Isolate the numerical values
actually convert them to double type
average them
I read something about the strtok function (just to start), but I would need some help...
You have started off on the right track and should be commended for using fgets() to read a complete line from the file on each iteration, but your choice of strstr does not ensure the prefix you are looking for is found at the beginning of the line.
Further, you want to avoid hardcoding your search string as well as the file to open. main() takes arguments through argc and argv that let you pass information into your program on startup. See: C11 Standard - ยง5.1.2.2.1 Program startup(p1). Using the parameters eliminates your need to hardcode values by letting you pass the filename to open and the prefix to search for as arguments to your program. (which also eliminates the need to recompile your code simply to read from another filename or search for another string)
For example, instead of hardcoding values, you can use the parameters to main() to open any file and search for any prefix simply using something similar to:
#include <stdio.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
int main (int argc, char **argv) {
char buf[MAXC] = "", *str = NULL; /* buffer for line and ptr to search str */
size_t n = 0, len = 0; /* counter and search string length */
double sum = 0; /* sum of matching lines */
FILE *fp = NULL; /* file pointer */
if (argc < 3) { /* validate 2 arguments given - filename, search_string */
fprintf (stderr, "error: insufficient number of arguments\n"
"usage: %s filename search_string\n", argv[0]);
return 1;
}
if (!(fp = fopen (argv[1], "r"))) { /* open/validate file open for reading */
perror ("fopen-filename");
return 1;
}
str = argv[2]; /* set pointer to search string */
len = strlen (str); /* get length of search string */
...
At this point in your program, you have opened the file passed as the first argument and have validated that it is open for reading through the file-stream pointer fp. You have passed in the prefix to search for as the second argument, assigned it to the pointer str and have obtained the length of the prefix and have stored in in len.
Next you want to read each line from your file into buf, but instead of attempting to match the prefix with strstr(), you can use strncmp() with len to compare the beginning of the line read from your file. If the prefix is found, you can then use sscanf to parse the double value from the file and add it to sum and increment the number of values stored in n, e.g.
while (fgets (buf, MAXC, fp)) { /* read each line into buf */
if (strncmp (buf, str, len) == 0) { /* if prefix matches */
double tmp; /* temporary double for parse */
/* parse with scanf, discarding prefix with assignment suppression */
if (sscanf (buf, "%*1023[^:]: %lf", &tmp) == 1) {
sum += tmp; /* add value to sum */
n++; /* increment count of values */
}
}
}
(note: above the assignment suppression operator for sscanf(), '*' allows you to read and discard the prefix and ':' without having to store the prefix in a second string)
All that remains is checking if values are contained in sum by checking your count n and if so, output the average for the prefix. Or, if n == 0 the prefix was not found in the file, e.g.:
if (n) /* if values found, output average */
printf ("prefix '%s' avg: %.4f\n", str, sum / n);
else /* output not found */
printf ("prefix '%s' -- not found in file.\n", str);
}
That is basically all you need. With it, you can read from any file you like and search for any prefix simply passing the filename and prefix as the first two arguments to your program. The complete example would be:
#include <stdio.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
int main (int argc, char **argv) {
char buf[MAXC] = "", *str = NULL; /* buffer for line and ptr to search str */
size_t n = 0, len = 0; /* counter and search string length */
double sum = 0; /* sum of matching lines */
FILE *fp = NULL; /* file pointer */
if (argc < 3) { /* validate 2 arguments given - filename, search_string */
fprintf (stderr, "error: insufficient number of arguments\n"
"usage: %s filename search_string\n", argv[0]);
return 1;
}
if (!(fp = fopen (argv[1], "r"))) { /* open/validate file open for reading */
perror ("fopen-filename");
return 1;
}
str = argv[2]; /* set pointer to search string */
len = strlen (str); /* get length of search string */
while (fgets (buf, MAXC, fp)) { /* read each line into buf */
if (strncmp (buf, str, len) == 0) { /* if prefix matches */
double tmp; /* temporary double for parse */
/* parse with scanf, discarding prefix with assignment suppression */
if (sscanf (buf, "%*1023[^:]: %lf", &tmp) == 1) {
sum += tmp; /* add value to sum */
n++; /* increment count of values */
}
}
}
if (n) /* if values found, output average */
printf ("prefix '%s' avg: %.4f\n", str, sum / n);
else /* output not found */
printf ("prefix '%s' -- not found in file.\n", str);
}
Example Use/Output
Using your data file stored in dat/prefixdouble.txt, you can search for each prefix in the file and obtain the average, e.g.
$ ./bin/prefixaverage dat/prefixdouble.txt hhhh
prefix 'hhhh' avg: 0.8874
$ ./bin/prefixaverage dat/prefixdouble.txt xxxx
prefix 'xxxx' avg: 0.9105
$ ./bin/prefixaverage dat/prefixdouble.txt yyyy
prefix 'yyyy' avg: 0.7962
$ ./bin/prefixaverage dat/prefixdouble.txt zzzz
prefix 'zzzz' avg: 0.7897
$ ./bin/prefixaverage dat/prefixdouble.txt foo
prefix 'foo' -- not found in file.
Much easier than having to recompile each time you want to search for another prefix. Look things over and let me know if you have further questions.
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.