How to read a file, pick up data and calculate - c

I am reading a book and I can't figure out this try it out : (it is in a non-english language so I translated it)
Write a program that ask for a number of students n, select n students (in a dynamic way), the name is 10 characters and note on 5 characters
Create a text file note.txt from the selection above and append hyphens to reach 10 characters (for the names).
Then read the file and from it (only), calculate the total. Then display the name and note of those that have a note that is not greater than 10.
You must implement 3 functions : createStudent, createFile and readFile, and not use global variables.
syntax : name must declared as char nom[10+1] (ie James, and then 5 hyphens will be added in order to get 10 characters) => james----- and note : char[5+1] (ie 15.00 or 07.50)
Tips : To convert the note from text format to float, you can use the atof function
I created the createStudent and createFile functions. they work well but I can't figure out the last part (readFile function).
My text file has this shape : Bart------ 04.50 Lisa------ 18.00 Homer----- 03.00
void readFile(int n){
FILE* file = NULL;
double temp= 0.0, average= 0.0;
double *total = (double*)malloc(n*sizeof(double));
int position = 0;
char information[5+1]="";
file = fopen("c:\\myFile.txt","r");
fseek(file,10,SEEK_SET);
while(fgetc(file) != EOF)
{
fscanf(file,"%5s",&information);
temp = atof(information);
total[position]= temp;
position++;
fflush(stdin);
fseek(file,11,SEEK_CUR);
}
fclose(file);
for(int compteur=0;compteur<2;compteur++)
{
moyenne += totalNote[compteur];
}
It compiles but doesn't work and I can't figure out why :(
I have the feeling that C language is such a pain in the ass compared to java or c#
Could you please give me some lights ?

It looks like your input file contains lines of the form " ". If there is always a fixed number of strings/numbers per line you can simply use fscanf (e.g. fscanf(file, "%*s %f %*s %f %*s %f", &number1, &number2, &number3);).
If, on the other hand, you can have an arbitrary number of string/number pairs per line, you should take a look at the strtok function.

You want to look into using strtok_r (or strtok if strtok_r not available). You can then convert your string into an array of token with space delimiter. Then it should be trivial to loop thur the array to convert and sum the amounts.

Use either fscanf or a combination of fgets,strtok,atol(or sscanf) to read the number.

Related

How do I read data from a text file and store it in my C program to be used in functions?

I'm trying to read different data types on the same line of a text file, and currently trying to store them in their own arrays via a structure. I'm not sure if this is the best course of action to begin with, but the point is to read data from a file and manipulate it using different functions. I thought that if I could extract the data from the file and store it in arrays, I could send the arrays into functions with the arrays as their parameters. Here's what I have, and the problem explained within the main function:
Driver File:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "student_struct.c"
struct Student{
char name[50];
int id;
float gpa;
int age;
};
int main(){
FILE *fptr;
fptr = fopen("student_records.txt", "r");
if (fptr == NULL){
printf("Error opening file!\n");
exit(1);
}
struct Student students[100];
int i = 0;
while(!feof(fptr)){
//PROBLEM HERE. Data for what is expected to be in the "gpa" array is always 0.
fscanf(fptr, "%c %d %f %d", &students[i].name[i], &students[i].id, &students[i].gpa, &students[i].age);
i++;
}
fclose(fptr);
//Always prints "0.0000"
printf("GPA of student #2: %f\n", students[1].gpa);
//avgGPA(students.gpa);
return 0;
}
Function:
#include <stdio.h>
float avgGPA(float gpa[]){
int i;
float avgGPA = 0;
for(i = 0; i < sizeof(*gpa); i++){
avgGPA += gpa[i];
}
avgGPA = avgGPA / sizeof(*gpa);
printf("Average GPA: %f", avgGPA);
}
Text file:
David 1234 4.0 44
Sally 4321 3.6 21
Bob 1111 2.5 20
Greg 9999 1.8 28
Heather 0000 3.2 22
Keith 3434 2.7 40
Pat 1122 1.0 31
Ann 6565 3.0 15
Mike 9898 2.0 29
Steve 1010 2.2 24
Kristie 2222 3.9 46
My question is, how do I properly pull the data from the file and use it in different functions? Thank you for your help.
The %c in fscanf needs to be changed to %s. Refer to the fscanf man page for what each of the conversion specifiers mean. Specifically:
s
Matches a sequence of non-white-space characters; the next pointer must be a pointer to character array that is long enough to hold the input sequence and the terminating null byte ('\0'), which is added automatically. The input string stops at white space or at the maximum field width, whichever occurs first.
c
Matches a sequence of characters whose length is specified by the maximum field width (default 1); the next pointer must be a pointer to char, and there must be enough room for all the characters (no terminating null byte is added). The usual skip of leading white space is suppressed. To skip white space first, use an explicit space in the format.
In other words, %c by default only matches a single character. %s matches multiple non-white space characters (ie, colloquially a "word").
Other follow on questions you had:
but if the array in the structure "Student" is made of characters, why does it properly function with a string?
In C a string is defined as an array of characters terminated by a NUL (0).
At that end, why does that influence the rest of the operation?
%c will consume just one character. Which means the next modifier (%d in this case) will try to match with the remaining part of the first word and fail.
Other best practices that are relevant should be applied. Specifically:
Always check the return value of function calls. fscanf in particular for this case. If that were done you would be able to see that fscanf had failed to match most of the modifiers.
while !feof is always wrong. A full explanation of that is not provided here but please refer to other SO answers such as this.
Use a debugger to step through your code to help you examine the state of variables to better understand what the program is doing and where things go wrong.

How to convert a values i get from a file (char) and store the values in to an array of double?

I am reading data from a file, retrieving how many columns and rows I have ( data file ), everything of so far. Now I am trying to read the values one by one and store the values in to a 2D array (double). I get the values as char using getc but when I try to use atoi or atof to convert the values from char to double i get strange values.
double ther[j][number];
char c;
int tim=0,nther=0;
FILE *fp3 = fopen("data.txt", "r");
c = getc(fp3) ;
while (c!= EOF)
{
ther[tim][nther]=atoi(&c);
printf("%lf", ther[tim][nther]);
nther++;
c = getc(fp3);
if(nther==number)
{
tim++;
nther=0;
}
tim=0;
}
fclose(fp3);
any suggestion?… (i keep searching). Sorry well i have a file data.txt and this file has rows an columns of numbers:
1 2 3 4 5
6 7 8 9 10
So i need to store the data into a 2D array: Thanks to the answers i have some ideas like: use an string, then divide all values and stored every one of them. 1st line is string-> array[0][0], array[0][1], etc. Then move to the other row and do the same.
Until now i get some numeric values but none of them are stored at the data.txt.
Firstly, it should be int c;. The getc() function has to be able to return a different value for all possible chars, and also a different value EOF (which signifies end-of-file, or stream error).
If you use char c;, then you must end up with EOF being converted to the value of a legal character, so you can't tell the difference between the two cases. In other words, your code might act like it hit EOF when in fact it just hit that particular character.
Moving on, atoi(&c) is not correct. If you read the documentation for the atoi function -- this is always a good idea when using functions -- you will find that it expects a string as input. However, &c is not a string. It is the address of a single char.
So your options are:
construct a string and pass it to atoi
don't use atoi
You didn't say what you were expecting to happen; but if you want to convert the character you read in of '3' to be the integer 3 then you can write: ther[tim][nther] = c - '0';.
However you should also have some code to handle cases when you read something that was not a number.
If you wanted to read in more than one character at a time (e.g. "31" -> 31) then the approach of using getc is not appropriate - you'll need to switch to using a function that reads multiple characters at once, such as fscanf or fgets.
Finally, you set tim to 0 every single loop iteration, so the tim++ never has any lasting effect, maybe this was an oversight.
You can read a double from a file using fscanf. An example would be:
double d;
fscanf("%f", &d);
fscanf can do much more than that so I'd recommend checking the documentation.
The method you're using gets a single character (as Matt McNabb pointed out - incorrectly). A character is 1 byte and can store the values 0-255. You're reading in text mode since you don't specify a mode with fopen (it is generally considered a good practice to explicitly ask for either binary "rb/wb" or text "rt/wt" mode). This means you read the ASCII value of a single digit.
So let's say the file contains:
7.2, 3.0, 1.0
The first call to getc(fp3) would get the character '7' which would have a value of 55 for ASCII. Obviously this is not going to give you what you're looking for.
Instead you could use:
double d[3];
fscanf("%f, %f, %f" &d[0], &d[1], &d[2]);
Which would get the three float values in the file.
#include <stdio.h>
int main(){
int j = 2;
int number = 5;
double ther[j][number];
double d;
int tim=0,nther=0;
FILE *fp3 = fopen("data.txt", "r");
while (fscanf(fp3, "%lf", &d)==1){
ther[tim][nther]=d;
printf("%g ", ther[tim][nther++]);
if(nther==number){
++tim;
nther=0;
printf("\n");
}
}
{
int r, c;
for(r=0;r<tim;++r){
for(c=0;c<number;++c){
printf("%f ", ther[r][c]);
}
printf("\n");
}
}
return 0;
}

Replace value in string with another from another string

I've been stuck for a while now. The program i'm writing basically changes the false words with the correct ones from the dictionary. However, when i run the program, it gives me no warnings or errors, but it doesn't display anything. Can you please help me?
#include<stdio.h>
#include<ctype.h>
#include<string.h>
int main(void){
char fname[20],word[2500], dictn[50];
int i,j;
float len1, len2;
FILE *inp, *dict, *outp, *fopen();
fpos_t pos1, pos2;
dict= fopen("dictionary.txt", "r");
printf("Enter the path of the file you want to check:\n");
scanf("%s", fname);
inp= fopen(fname, "r");
for(i=0;(fscanf(inp, "%s", word) != EOF); i++){
for(j=0;fscanf(dict, "%s", dictn) != EOF; j++){
fgetpos(inp, &pos1);
fgetpos(dictn, &pos2);
len1=(float)strlen(word);
len2=(float) strlen(dictn);
if(len1<=(0.6*len2)){
fsetpos(dictn, &pos1);
}
if(strncmp(word, dictn, 1)==0){
fsetpos(dictn, &pos1);
}
if(strcmp(word, dictn)==0){
fsetpos(dictn, &pos1);
}
}
printf("%s ", word);
}
fclose(inp);
fclose(dict);
return(0);
}
You can use
sprintf(word, "%s ", dictn);
If your code is working with printf it should work with sprintf, provided you don't overflow "word", including the NULL termination, so you might have to resize "word" if it is smaller than dictn.
First of all, I'm assuming you have created arrays word and dictn with enough size to hold the maximum length string any of your files.
First fault:
In loops you've created, i represents number of strings in input file and j represents number of strings in dictionary. word is your input string variable and dictn is your dictionary string variable. But you want to retrieve and alter word's ith or dictn's jth character. This may cause an error because there can be a case like this:
Suppose there are 10 words at inp file and 100 words at dictn. And in your loops, i have value of 8 and j have value of 88. Corresponding these i and j values, word has string value of, say, apple and dictn has string value of apple also. So this means apple is the 8th word at input file and 88th word at dictionary file. And if one of those if conditions was satisfied, compiler tries to apply a statement like word[i]=dictn[j];. This means word[8] = dictn[88]; for this example. But both of those string have apple as values which consists only 5 characters! And this will cause an error since you've tried to retrieve 88th character of a 5-length string and assign it to the 8th character of a 5-length string. So your code is wrong, it will only work for some cases which will be a rare situation.
Second fault:
I assume you want to read whole dictionary file for every word in input file but you will be able to read it for only first word of input file since you don't reopen it or set position indicator at the beginning of dictionary file after you read whole dictionary.
Third fault:
Your first if statement will never be reached assuming you have created len1 and len2 variables as integers. Because in your if statement, there is a multiplication of a decimal number and an integer which will return 0 as a result and since fscanf() ignores whitespaces, len1 and len2 will be at least 1.
Fourth fault:
Also your else if statement will never be reached because if a string has same value with another, their first character will also be equal to each other and your if statement where you compare their first characters will be also accepted.
Actually, I would write a code as solution but first of all you need to correct things up which are logically wrong because I do not know what you are really try to achieve by your code -just because I commented with full of assumptions-. But I can provide you some guidelines:
Convert your len1 and len2 variables from int to float and cast values which return from strlen() functions to float.
Reopen your dict file for every iteration of outside loop. (And do not forget not to close it).
To change your inp file, you can use a fpos_t type of variable to track your position indicator of your inp file (fgetpos() to get current position and fsetpos() to change position with value of fpos_t variable. You can search them.) and type the word with fprintf() or fputs() to that location to change that string.

Reading values from CSV file into variables

I am trying to write a simple piece of code to read values from a CSV file with a max of 100 entries into an array of structs.
Example of a line of the CSV file:
1,Mr,James,Quigley,Director,200000,0
I use the following code to read in the values, but when I print out the values they are incorrect
for(i = 0; i < 3; i++) /*just assuming number of entries here to demonstrate problem*/
{
fscanf(f, "%d,%s,%s,%s,%s,%d,%d", &inArray[i].ID, inArray[i].salutation, inArray[i].firstName, inArray[i].surName, inArray[i].position, &inArray[i].sal, &inArray[i].deleted);
}
Then when I print out the first name, the values are all assigned to the first name:
for(j = 0; j < 3; j++) /* test by printing values*/
{
printf("Employee name is %s\n", inArray[j].firstName);
}
Gives ames,Quigley,Director,200000,0 and so on in that way. I am sure it's how i format the fscanf line but I can't get it to work.
Here is my struct I'm reading into:
typedef struct Employee
{
int ID;
char salutation[4];
char firstName[21];
char surName[31];
char position[16];
int sal;
int deleted;
} Employee;
This is because a string %s can contain the comma, so it gets scanned into the first string. There's no "look-ahead" in the scanf() formatting specifier, the fact that the %s is followed by a comma in the format specification string means nothing.
Use character groups (search the manual for [).
const int got = fscanf(f, "%d,%[^,],%[^,],%[^,],%[^,],%d,%d", &inArray[i].ID,
inArray[i].salutation, inArray[i].firstName,
inArray[i].surName, inArray[i].position, &inArray[i].sal,
&inArray[i].deleted);
And learn to check the return value, since I/O calls can fail! Don't depend on the data being valid unless got is 7.
To make your program read the entire file (multiple records, i.e. lines), I would recommend loading entire lines into a (large) fixed-size buffer with fgets(), then using sscanf() on that buffer to parse out the column values. That is much easier and will ensure that you really do scan separate lines, calling fscanf() in a loop will not, since to fscanf() a linefeed is just whitespace.
Might as well post my comment as an answer:
%s reads a full word by default.
It finds the %d, the integer part, then the ,, and then it has to read a string. , is considered valid in a word (it is not a whitespace), so it reads until the end of the line (there is no whitespace until then), not until the first comma... And the rest remains empty. (From this answer)
You have to change the separator with specifying a regex:
fscanf(f, "%d,%[^,],%[^,],%[^,],%[^,],%d,%d", &inArray[i].ID, inArray[i].salutation, inArray[i].firstName, inArray[i].surName, inArray[i].position, &inArray[i].sal, &inArray[i].deleted);
Instead of %s, use %[^,], which means "grab all chars, and stop when found a ,".
EDIT
%[^,]s is bad, it would need a literal s after the end of the scanset... Thanks #MichaelPotter
(From Changing the scanf() delimiter and Reading values from CSV file into variables )

reading multiple variable types from single line in file C

Alright I've been at this all day and can't for the life of me get this down, maybe you chaps can help. I have a file that reads as follows
1301,105515018,"Boatswain","Michael R.",ABC, 123,="R01"
1301,103993269,"Castille","Michael Jr",ABC, 123,="R03"
1301,103993267,"Castille","Janice",ABC, 123,="R03"
1301,104727546,"Bonczek","Claude",ABC, 123,="R01"
1301,104731479,"Cruz","Akeem Mike",ABC, 123,="R01"
1301,105415888,"Digiacomo","Stephen",ABC, 123,="R02"
1301,106034479,"Annitto Grassis","Susan",ABC, 123,="R04"
1301,106034459,"Als","Christian",ABC, 123,="R01"
And here is my code...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_NAME 15
#define MAX_SUBSEC 3
#define N 128
//void printArr(struct *students);
struct student{
int term;
int id;
char lastname[MAX_NAME];
char firstname[MAX_NAME];
char subjectname[MAX_SUBSEC];
int catalog;
char section[MAX_SUBSEC];
}students[10];
int main(){
int term;
int id;
char lastname[MAX_NAME];
char firstname[MAX_NAME];
char sub[MAX_SUBSEC];
int cat;
char sec[MAX_SUBSEC];
char fname[N];
FILE *inputf;
printf("Enter the name of the text file: ");
scanf("%123s",fname);
strcat(fname,".txt");
inputf = fopen(fname,"r");
if (inputf == NULL){
printf("I couldn't open the file for reading.\n");
exit(0);
}
//TROUBLE HERE!
fscanf(inputf, "%d,%d,%[^,]s", &students[0].term, &students[0].id,students[0].lastname);
printf("%d\n", students[0].term);
printf("%d\n", students[0].id);
printf("%s\n", students[0].lastname);
/*for (j = 1 ; j <= 10-1 ; j++){
for(k = 0 ; k <= 10-2 ; k++){
if(students[k] > students[k+1]){
temp = students[k];
students[k] = students[k+1];
students[k+1] = temp;
}
}
}*/
fclose(inputf);
system("pause");
return 0;
}
void printArr(int a[], int tally){
int i;
for(i = 0 ; i < tally ; i++){
printf("%d ", a[i]);
}
printf("\n");
}
My objective is to take each one of those values in the text file and input it to where it belongs in the struct and subsequently the struct array, but I can't get passed the first 2 ints.
Getting the lastname string, because it is a max of 15 characters, it spills over into the first name string right after it and takes what remaining characters it needs in order to fill up the lastname char array. Obviously I do not want this. As you can see I have tried strtok but it doesnt do anything, not sure what I have to do though as I have never used it before. Also have tried just including all the variables into fscanf statement, but I either get the same output, or it becomes a mess. As it is, I am extremely lost, how do I get these values into the variables they belong?!
EDIT: updated my code, I have gotten a little farther but not much. I can now print out just the last name but can not more farther from there, I cant get to the firstname string or any of the variables beyond it.
What you have there is a CSV file with quoted strings, and so I would recommend you use a CSV parser (or roll your own) rather than trying to do it all with scanf (since scanf cannot deal with quotes, e.g. commas within quoted strings). A quick Google search turns up libcsv.c which you may be able to use in your project.
With the fscanf format string "%d,%d,\"%[^\"]\",\"%[^\"]\",%[^,],%d,=\"%[^\"]\"" we can read a whole line's data. Besides, you have to define
char lastname[MAX_NAME+1];
char firstname[MAX_NAME+1];
char subjectname[MAX_SUBSEC+1];
int catalog;
char section[MAX_SUBSEC+1];
— the +1 to account for the terminating '\0' character.
I have a question for you... If you want to know how to use a diamond cutter, do you try it and see, or do you consult the manual? The problem here isn't the result of your choice, but your choice itself. Believe it or not, I have answered these questions so often that I'm tired of repeating myself. The answer is all in the manual.
Read the POSIX 2004 scanf manual — or the POSIX 2008/2013 version — and the answer this question and you'll have some idea of what you're not doing that you should be. Even fscanf code should use assert as a debugging aid to ensure the number of items read was correct.
%[^,]s It seems as though there's a mistake here. Perhaps you meant %[^,]. The %[ format specifier is a different format specifier to the %s format specifier, hence in the presumably mistaken code there are two directives: %[^,] and s. The s directive tells scanf to read an 's' and discard it.
1.There is a syntax error in
while(result != NULL){
printf(".....);
......
}
}//error
fscanf(inputf, "%s", lastname); can't read a line ,fscanf will stop when it comes across an space
fscanf reads one line at a time, and you can easily capture the contents of each line because your file is formatted pretty nicely, especially due to the comma separation (really useful if none of your separated values contain a comma).
You can pass fscanf a format like you're doing with "%d" to capture an int, "%s" to capture a string (ends at white space, be weary of this when for example trying to find a name like "Annitto Grassis, which would require 2 %s's), etc, from the currently read line of the file. You can be more advanced and use regex patterns to define the contents you want captured as chars, such as "Boatswain", a sequence comprised chars from the sets {A-Z}, {a-z}, and the {"}. You'll want to scan the file until you reach the end (signified by EOF in C) so you can do such and capture the contents of the line and appropriately assign the values to variables like so:
while( fscanf(inputf, "%d,%d,%[\"A-Za-z ],%[\"A-Za-z .]", &term, &id, lastname, firstname) != EOF) {
.... //do something with term, id, lastname, firstname - put them in a student struct
}
For more about regex, Mastering Regex by Jeff Friedl is a good book for learning about the topic.

Resources