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

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.

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);

Read Line By Line Until Integer is Found C

Trying to create a program that takes in a text file and reads it line by line. It then finds the two integers that are on each line and adds them together. It then outputs the new line with the original string and total to a new text file. I need help adding the two integers, getting them from each line, and then putting the new line to a text file.
input text file
good morning hello 34 127
ann 20 45
10 11
fun program and you find the same 90 120
news paper said that 56 11
how do you like 20 5
line number 90 34
Outputs first like would look like: and then continue on
good morning hello 161
Code:
int processTextFile(char * inputFileName, char * outputFileName)
{
FILE *fp = fopen(inputFileName, "r");//open file to to read
char buff[1024];
char *p, *p1;
int num;
while (fgets(buff, 1024, fp)!=NULL)
{
printf("%s\n", buff);
while(scanf(buff, "%*[^0-9]%d", &num)== 1)
printf("%d\n", num);
//fscanf(fp, "%s", buff);
}
return 0;
}
EDIT!!!!::
So now that I've been able to accomplish this. How would I sort it by the number produced? for example:
Time is money 52
here I am 3
21
Would output to a new text file in order like
here I am 3
21
Time is money 52
My version using strcspn() is supposed to work with stdin for input and stdout for output. (so you can do executable <textfile >newtextfile)
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char line[1000];
while (fgets(line, sizeof line, stdin)) {
char *ptr;
size_t x = strcspn(line, "0123456789");
if (line[x]) {
errno = 0;
int n1 = strtol(line + x, &ptr, 10);
if (*ptr && !errno) {
errno = 0;
int n2 = strtol(ptr, &ptr, 10);
if (*ptr && !errno) {
int n3 = n1 + n2;
printf("%.*s%d\n", (int)x, line, n3);
} else {
printf("%s", line); // line includes ENTER
}
} else {
printf("%s", line); // line includes ENTER
}
} else {
printf("%s", line); // line includes ENTER
}
}
return 0;
}
The same version without the error checking
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char line[1000];
while (fgets(line, sizeof line, stdin)) {
char *ptr;
size_t x = strcspn(line, "0123456789");
int n1 = strtol(line + x, &ptr, 10);
int n2 = strtol(ptr, &ptr, 10);
int n3 = n1 + n2;
printf("%.*s%d\n", (int)x, line, n3);
}
return 0;
}
The approach should be:
open two files, one for input, one for output.
use sscanf() to read the input buffer.
scan the leading string, and then two number.
if previous sscanf() fails, only check for two number.
if either of the above scanning is success, print the sum to the output file.
A sample code, should look like
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
FILE *fpin = fopen("ipfile", "r");//open file to to read
if (!fpin)
{
printf("Error in ipfile opening\n");
exit (-1);
}
FILE *fpout = fopen("opfile", "w");//open file to to write
if (!fpout)
{
printf("Error in opfile opening\n");
exit (-1);
}
char buff[1024] = {0};
char str[1024] = {0};
int num1 =0, num2= 0;
while (fgets(buff, 1024, fpin)!=NULL)
{
memset(str, 0, sizeof(str));
//printf("%s\n", buff);
if(sscanf(buff, "%[^0-9]%d %d", str, &num1, &num2)== 3)
fprintf(fpout, "%s %d\n", str, (num1+num2));
else if (sscanf(buff, "%d %d", &num1, &num2)== 2)
fprintf(fpout, "%d\n", (num1+num2));
}
return 0;
}
Note:
The above procedure, is a kind of workaround. If the data pattern in the file changes, lot of changes will be required to maintain a code like that. Instead of usinf sscnaf(), for a better and roubust approach, you should
read the line from file
start tokenizing the input buffer (strtok()) and check for ints as tokens (strtol()).
save the returned tokens and ints seperately.
once the strtok() returns NULL, you print the string tokens and the sum of the ints to the o/p file.
I intend not to change your code completely, so I just added some snippets for improvement.
int processTextFile(char *inputFileName, char *outputFileName) {
FILE *fp = fopen(inputFileName, "r");
FILE *out = fopen(outputFileName, "w");
char line[1024];
if (!fp) {
perror(inputFileName);
return;
}
while (fgets(line, sizeof(line), fp) != NULL) {
int num1 = 0, num2 = 0;
char textPart[1024] = "";
if ( !sscanf(line, "%[a-zA-Z' ']%d%d", textPart, &num1, &num2) {
sscanf(line, "%d%d", &num1, &num2);
}
fprintf(out, "%s %d\n", textPart, num1 + num2);
}
fclose(fp);
fclose(out);
}
Explanation:
I scanned the text file, extracted the text part and the two ints. Since I noticed that the ints are placed at the end of each line, I just used sscanf() for that matter.
sscanf(line, "%[a-zA-Z' ']%d%d", textPart, &num1, &num2);
Here, "%[a-zA-Z' ']%d%d" format specifiers means to get only alphabets and spaces.
Since it will only get letters and spaces, the line "10 11" in your input file won't be put to num1 and num2. Because the code inspects first for a string containing letters and spaces. Since 10 and 11 are not of the qualified types, then the line is just skipped.
That's why I added an if-else statement, which checks if sscanf wrote anything to memory. If sscanf returned 0, then it means that no text part is present. Just digits. So the program will scan the two digits.
if ( !sscanf(line, "%[a-zA-Z' ']%d%d", textPart, &num1, &num2) ) {
sscanf(line, "%d%d", &num1, &num2);
}
I also added file checking for input file. It checks if file doesn't exist or can't be opened by the filestream.
if (!fp) {
perror(inputFileName);
return;
}
Here is the content of output file after execution:
good morning hello 161
ann 65
21
fun program and you find the same 210
news paper said that 67
how do you like 25
line number 124

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.

reading primitives from file in 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.

Resources