reading primitives from file in C - c

I am new to C, and want to read some data from a file.
Actually, I find many reading functions, fgetc, fgets, etc..
But I don't know which one/combination is the best to read a file with the following format:
0 1500 100.50
1 200 9
2 150 10
I just need to save each row above into a struct with three data members.
I just need to know the best practice to do that, hence I am new to C programming.
Thanks.

Try reading each line using fgets. With each line, you can then use sscanf.
FILE* f = fopen("filename.txt", "r");
if (f) {
char linebuff[1024];
char* line = fgets(linebuff, 1024, f);
while (line != NULL) {
int first, second;
float third;
if (sscanf(line, "%d %d %g", &first, &second, &third) == 3) {
// do something with them..
} else {
// handle the case where it was not matched.
}
line = fgets(linebuff, 1024, f);
}
fclose(f);
}
This may have errors, but it's just meant to give you an example of how you might use the functions. Be sure to validate what sscanf returns you.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void
read_file(const char *fname)
{
FILE *f;
char line[1024];
int lineno, int1, int2, nbytes;
double dbl;
if ((f = fopen(fname, "r")) == NULL) {
perror("fopen");
exit(EXIT_FAILURE);
}
for (lineno = 1; fgets(line, sizeof line, f) != NULL; lineno++) {
int fields = sscanf(line, " %d %d %lg %n", &int1, &int2, &dbl, &nbytes);
if (fields != 3 || (size_t) nbytes != strlen(line)) {
fprintf(stderr, "E: %s:%d: badly formatted data\n", fname, lineno);
exit(EXIT_FAILURE);
}
/* do something with the numbers */
fprintf(stdout, "number one is %d, number two is %d, number three is %f\n", int1, int2, dbl);
}
if (fclose(f) == EOF) {
perror("fclose");
exit(EXIT_FAILURE);
}
}
int main(void)
{
read_file("filename.txt");
return 0;
}
Some notes on the code:
The fscanf function is quite difficult to use. I had to experiment a while until I got it right. The space characters between the %d and %lg are necessary so that any white-space between the numbers is skipped. This is especially important at the end of the line, where the newline character must be read.
Most of the code is concerned with checking errors thoroughly. Almost every return value of a function call is checked whether it succeeded or not. In addition, the number of fields and the number of characters that have been read are compared to the expected values.
The format strings for fscanf and fprintf differ in subtle details. Be sure to read the documentation for them.
I used the combination of fgets to read one line at a time and sscanf to parse the fields. I did this because it seemed impossible to me to match a single \n using fscanf.
I used the GNU C Compiler with the standard warning flags -Wall -Wextra. This helped to avoid some easy mistakes.
Update: I forgot to check that each invocation of fgets reads exactly one line. There might be lines that are too long to fit into the buffer. One should check that the line always ends with \n.

Related

get 3 (max) digit integer from txt file

First of all I am new to files in c, so it may be a simple question,
however I still didn't find a solution:
let's say that's the content of my file:
99
blah blah
...
...
I want to scan only the number from the beginning (it is always in a separate line)
My question is how to make it take the number (99) as one number and stop scanning.
int main(){
FILE* fp = fopen(file_name, "r");
int integer;
...
fclose(fp);
printf("%d", integer);
}
output for the file example:
99
-the nuber can be between 1 and 100-
I want to scan only the number from the beginning (it is always in a separate line).
That's a good hint, suggesting a line by line parsing of the input. You can use a combination of fgets(1) and sscan(2) to read that number.
fgets will read up to a certain number of character from a stream and store those character into a buffer. If it finds a newline, it stops reading, store the newline into the buffer followed by the null-terminator. Otherwise it only adds the terminator. If it fails, it returs a NULL pointer.
sscanf works basically like scanf or fscanf, but it reads from a character array, not from a stream.
It's also better to always check the return value of those library function.
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUF_SIZE 1024
int main(void)
{
char const *file_name = "data.txt";
FILE *in_file = fopen(file_name, "r");
if (!in_file) {
fprintf(stderr, "Error while reading \"%s\": %s", file_name, strerror(errno));
return EXIT_FAILURE;
}
char buffer[BUF_SIZE];
int number = 0;
while( fgets(buffer, BUF_SIZE, in_file) ) {
if ( sscanf(buffer, "%d", &number) == 1 ) {
if ( 0 < number && number < 100 ) {
printf("%d", number);
break;
}
}
}
fclose(in_file);
return EXIT_SUCCESS;
}
Example.
Some references of the functions used in the previous snippet
1) fgets: man-pages or cppreference.
2) sscanf: man-pages or cppreference
Why not use scanf? (fscanf to be more precise):
On success, the function returns the number of items of the argument
list successfully filled.
(source: cppreference)
So just check how many values did you read, if 0 that means it's not a number so you can just skip that string, for that you can use "%*" prefix to ignore the data.
You also said:
I want to scan only the number from the beginning (it is always in a
separate line)
so after you read the number just skip the whole line with "%*[^\n]" (reads
data until a new line symbol is encountered)
int num;
int scanReturn;
FILE* f = fopen("file.txt", "r");
...
do {
scanReturn = fscanf(f, "%d", &num);
if(scanReturn == 0)
{
scanReturn = fscanf(f, "%*s");
}
else if(scanReturn != EOF)
{
fscanf(f, "%*[^\n]");
printf("%d, ", num);
}
} while(scanReturn != EOF);
fclose(f);

trouble reading tokens in a file

i'm doing a homework assignment that requires reading from an input file. the program just exits with error, however, and i can't tell why. here's my code.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
int main()
{
FILE *fp = fopen( "input.txt", "r");
FILE *outputF = fopen( "output.txt", "w");
if( !fp || !outputF){
fprintf(stderr, "can't open files for input/output");
exit(EXIT_FAILURE);
}
char line[250];
double factor;
int expo;
while( fscanf( fp, "%f%d", factor, expo) == 1){
if( factor == 0){
fprintf(outputF, "%s\n", "undefined");
}
else{
double total = 1;
for(int i = 0; i < expo; i++){
total = total * factor;
}
fprintf(outputF, "%f", total);
}
}
fclose(fp);
fclose(outputF);
return EXIT_SUCCESS;
}
i'm thinking the problem is with the "while" line but i also tried it with the following code and it did not work. the input file has a doulbe and an int seperated by a space. i.e. "2.33 3"
while(fscanf(fp, "%s", line) == 1){
char *token;
token = strtok(line, " ");
float factor;
sscanf(token, "%f", &factor);
token = strtok(NULL, "\n");
int expo;
sscanf(token, "%d", &expo);
First problem while (fscanf( fp, "%f%d", factor, expo) == 1) has to be
while (fscanf(fp, "%f%d", &factor, &expo) == 2)
read fscanf()'s manual. It does not return a truth value it returns the number of matched specifiers in the string.
Second problem, undefined behavior due to the incorrect scanf() format specifier, for double you need "%lf" and not "%f".
Third problem, you must pass the address of the values you want to read to allow scanf() to store the result in those variables, that's what the & are doing in the fixed fscanf() above.
Note: Your compiler should warn about two of these errers, the wrong format specifier and not using the & address of operator for these particular specifiers. There are two options, you are ignoring these warnings or you are compiling with warnings turned off. The first of these possible reasons is really bad, do not ignore warnings. The second, read the documentation for your compiler and enable as much diagnostics as possible to avoid silly mistakes.

Program reads file but "skips" lines when they shouldn't be skipped

So basically I have a program that reads in a specific format of a file as shown:
P3
# CREATOR: GIMP PNM Filter Version 1.1
400 530
255
189
165
181
181
156
...
My program reads in the first line as the format of the file, then any comments starting with a '#', then the width and height, then the max rgb value and then any other rgb values.
NOTE: the part reading in the rgb values is incomplete so ignore this for my question. Now to my issue..
The output i want is as shown as the exact file format, but the result i get is as follows:
P3
# CREATOR: GIMP PNM Filter Version 1.1
255 189
165
As you can see, it completely skips the width and height values and uses the next 2 values in place of it for some reason...
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define COMMENT_LENGTH 256
#define COMMENT_ARRAY_SIZE 10
#define MAX_PIXEL_HEIGHT 480
#define MAX_PIXEL_WIDTH 640
typedef struct PPM {
char format[2]; //2 letter code for PPM format
char comments[COMMENT_LENGTH][COMMENT_ARRAY_SIZE]; //comment array
int width; //number of columns
int height; //number of rows
int max; //maximum colour value (usually 255)
int rgbPixels[MAX_PIXEL_WIDTH * MAX_PIXEL_HEIGHT][3]; //integers between 0 and max for pixel i's RGB values
}PPM;
struct PPM * getPPM(FILE *fd);
int main(int argc, char **argv) {
FILE *file = fopen("ape.ppm", "r");
if(file == NULL) return 0;
else {
struct PPM *newPPM = getPPM(file);
return 0;
}
}
struct PPM * getPPM(FILE *fd) {
if(fd == NULL) {
return NULL;
}
struct PPM *newPPMFile = (PPM *) malloc(sizeof(PPM));
fscanf(fd, "%2s\n", newPPMFile->format);
printf("%2s\n", newPPMFile->format);
//check for comments
int i = 0;
char str[COMMENT_LENGTH];
while(i < COMMENT_ARRAY_SIZE && fgets(str, COMMENT_LENGTH, fd) != NULL) {
if(str[0] != '#') {
break;
} else {
strcpy(newPPMFile->comments[i], str);
printf("%s", newPPMFile->comments[i]);
}
}
//read width and height
fscanf(fd, "%d %d", &newPPMFile->width, &newPPMFile->height);
printf("%d %d\n", newPPMFile->width, newPPMFile->height);
//read max
fscanf(fd, "%d", &newPPMFile->max);
printf("%d\n", newPPMFile->max);
//read rgb data in rgb array (INCOMPLETE)
//close file
fclose(fd);
return newPPMFile;
};
There are a couple things here:
while(i < COMMENT_ARRAY_SIZE && fgets(str, COMMENT_LENGTH, fd) != NULL) {
if(str[0] != '#') {
break;
The position for the next read (offset) has already advanced, if you break or not. So naturally, it'll continue at the advanced offset, like you experienced here.
Also,
(PPM *) malloc(sizeof(PPM));
Casting the result of malloc() is unnecessary and potentially masks errors.
fgets reads the full line into the buffer. It it doesn't begin with a hash mark, you lose the data on that line.
A solution is to use fscanf throughout. fscanf stops scanning when the pattern isn't matched or if a conversion can't be made. So if you enforce a hash mark before the comment, but the line doesn't begin with a hash mark, the rest of the line is untouched:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
int width, height, max;
char buf[80]; // temporary buffer
FILE *fd = fopen("kk", "r");
if(fd == NULL) return 1; // TODO: Error message
fscanf(fd, "%79s", buf);
if (strcmp(buf, "P3") != 0) return 1; // TODO: Error message
while (fscanf(fd, " # %79[^\n]%*[^\n]", buf) == 1) {
printf("Comment: %s\n", buf);
}
fscanf(fd, "%d %d", &width, &height); // TODO: Check success
printf("%d x %d\n", width, height);
fscanf(fd, "%d", &max); // TODO: Check success
printf("max: %d\n", max);
fclose(fd);
return 0;
}
There are some things to keep in mind, though:
fscanf does not care or know about new lines; new-line characters are just white space. That means that your first hash mark could be on the same line with the file format P3.
What is an advantage in reading the comments may be a pitfall when you read the values: The code above doesn't check whether a conversion was made. That's not good, especially that if you read EOF or a string that isn't an integer, fscanf gets stuck.
The format %79[^\n] will read only up to 79 characters. If the comment line is longer, the file position will not be on the new-line character, this srewing up the reading of subsequent lines. To circumvent that, you can add %*[^\n] after that: This will enforce that you read to the next new-line character, but it won't store the results. (* means "don't store result" in scanf formats.) That way you get up to the first 80 characters of each comment line and also ensure that the line is read completely if it is longer.
Insufficient space #errikos.
// char format[2]; //2 letter code for PPM format
char format[2 + 1]; //2 letter code for PPM format
...
// 2 is the max width of non-white-space characters to scan
fscanf(fd, "%2s\n", newPPMFile->format);
Check the return values form fscanf()
// fscanf(fd, "%d %d", &newPPMFile->width, &newPPMFile->height);
if (fscanf(fd, "%d %d", &newPPMFile->width, &newPPMFile->height) != 2) {
fprintf(stderr, "Error reading width & height\n");
}
Code mixes fscanf() with fgets(). Tis often is a problem as many formats used to not consume that trailing '\n`` and then a followingfgets()simply reads just that one character. Reccomedn only usingfgets()`.
Possibly other important issues too.
here is the root of the problem.
in the loop reading comments:
when the line is read (when reading comments) that does not start with a #, then the code has already read width+height values from the input file.
So the code needs to parse those values from the already read line.

Read a file then store numbers in array C

so I have this file called "score.txt" with contents
NAME
20
NAME2
2
And I'm using this code but it gets an error and I have no idea on how to put the integers from the file in an array.
int main(){
FILE* file = fopen ("score.txt", "r");
int i = 0;
fscanf (file, "%d", &i);
while (!feof (file))
{
printf ("%d ", i);
fscanf (file, "%d", &i);
}
fclose (file);
system("pause");
}
I'm only self learning and i've been trying to figure this out for 2hours already
The problem with using fscanf for input where some lines will fail the format is that the file will not be advanced per iteration of the while loop, so you get stuck.
You can get a solution by using fgets to grab the data and sscanf to grab the number:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main(void) {
int i = 0;
int ret = 0;
char buf[50];
FILE *file = fopen("score.txt", "r");
if (file == NULL) {
fprintf(stderr,"Unable to open file\n");
exit(1);
}
while (fgets(buf,sizeof(buf),file)) {
ret = sscanf(buf,"%d",&i);
if (ret == 1) { // we expect only one match
printf("%d\n", i);
} else if (errno != 0) {
perror("sscanf:");
break;
}
}
fclose(file)
return(0);
}
This will output, for your input:
20
2
We check the output of sscanf as it tells us if the format has been matched correctly, which will only happen on the lines with integer, and not the 'NAME' lines. We also check for 'errno' which will be set to non-zero if sscanf encounters an error.
We used char buf[50]; to declare a char array with 50 slots, which fgets then uses to store the line its reading; however if the line is more than 50 chars in length it will be read in 50 char chunks by fgets, and you may not get the results you desire.
If you wish to store the integers you read into an array, you'll have to declare an array, then on each read assign a slot in that array to the value of the int you read i.e. int_array[j] = i (where j will have to change with each slot you use). I'll leave it as an exercise to implement this.

How to read in a text file of tab-separated integers in C?

I have a file of simply tab-separated integers (a .txt file) and I wish to read them in with just C, line by line. So, say each line has 5 integers. How can I accomplish this?
My first attempt was as follows. It was just to read in a single integer, but even that didn't work:
FILE *fp;
char blah[255];
int *some_int;
fp = fopen("test.txt", "rt");
while (fgets(blah, 255, fp) != NULL)
{
sscanf(blah, "%d", some_int);
printf("%d\n", *some_int);
}
Here's a way no one else suggested, that doesn't use fscanf so you can have sane error handling:
char buffer[BUFSIZE];
size_t size = 5;
int *data = malloc(size * sizeof *line);
if(line == NULL) error();
while(fgets(buffer, sizeof buffer, fp)
{
size_t i = 0;
char *next = buffer;
while(*next && *next != '\n')
{
data[i++] = strtol(next, &next, 0);
// check for errors
}
}
Basically, instead of trying to use *scanf's "%d" to read characters, use the function it (probably) calls to do the conversion: strtol. Where *scanf goes through the string to match the format string but doesn't let you "save your place" in between function calls, strtol does, which is what you need to read an arbitrary number of integers.
I haven't written all your code for you - you have to do the hard error handling. Possible errors include:
i == size, in which case you can try to make data bigger with realloc. Alternately, you could loop through the buffer and count how many numbers there are beforehand, then allocate that many so you don't need to reallocate later.
fgets didn't read the entire line (check that the last character before '\0' is '\n'). In this case you'll probably want to refill the buffer and keep reading numbers. Be careful in this case - you'll likely need to go back and recalculate the last number - fgets might have cut it off. (This is one disadvantage to using fgets.)
Erroneous input - handle however you like.
#include <stdio.h>
int main(){
FILE *fp;
int scanned = 0;
int some_ints[5];
fp = fopen("test.txt", "r");
while ((scanned = fscanf(fp, "%d %d %d %d %d", some_ints, some_ints+1, some_ints+2, some_ints+3, some_ints+4)) != EOF) {
if(scanned ==5){
printf("%d %d %d %d %d\n", some_ints[0], some_ints[1], some_ints[2], some_ints[3], some_ints[4]);
}
else {
printf("Whoops! Input format is incorrect!\n");
break;
}
}
}
I'd do something like this:
int storedVals[MAX_STORED_VALS];
int bf;
int ii=0;
while (!feof(fp) && ii<MAX_STORED_VALS) {
if (fscanf(fp," %d",&bf)) {
storedVals[ii++]=bf;
}
}
fscanf automatically does white space trimming. So as long as there's a space in your scan string, it'll get rid of zero or more \t (tabs) and \n (newlines) to find the next integer. Of course, this doesn't do much by way of error correction.

Resources