C - Read string with integers from formatted text file - c

I have a text file following this format:
Thing 1: 0 0 128
Other thing: 255 64 255
Something else: 32 32 8
I intend to add more to this file eventually but the format will remain the same. What I want to do is read everything before the colon into a string and everything after it as an integer. I've tried this:
fscanf((file = fopen("colors.txt", "r")) == NULL){
return -1;
}
fscanf("%s: %d %d %d", colorStr, &r, &g, &b);
while(!feof(file)){
printf("%s: %d %d %d", colorStr, r, g, b);
fscanf(file, "%s: %d %d %d", colorStr, &r, &g, &b);
}
fclose(file);
However, I get this output:
Thing 1:: 0 0 0
0: 0 0 0
0: 0 0 0
128: 0 0 0
And so on. Ideally, the output should read like this:
Thing 1: 0 0 128
Other thing: 255 64 255
Something else: 32 32 8
How can I fix this? The colorStr, r, g, and b variables were set up earlier in the program.

The problem with your code is that the text contains spaces, which %s does not allow.
Changing the format string to %[^:] will fix this problem.
However, the code would remain vulnerable to buffer overrun. Make sure that your format string includes the max size of colorStr to prevent it:
char colorStr[100];
fscanf(file, " %99[^:]: %d %d %d", colorStr, &r, &g, &b);
Your code uses feof(file), which is incorrect. You should put fscanf into loop header. This would let you remove the duplicate fscanf call before the loop:
while(fscanf(file, " %99[^:]: %d %d %d", colorStr, &r, &g, &b) == 4) {
printf("%s: %d %d %d\n", colorStr, r, g, b);
}
Note the space in front of the leading % format specifier. It instructs fscanf to skip the trailing spaces and/or '\n' from the previous line.
Demo.

Related

Can you use 'fscanf' to read in individual chars and an int?

I am trying to convert some code that reads in three integers per line of txt file til end of file.
ie:
int int int
int int int
int int int
What I am trying to get it to do now is read in is:
z z 5
z z 6
z z 5
til end of file.
I'm having an issue right after it opens the file. I placed several debuggers to track the progress and I commented what is printed. It only shows the before and after of the first line of the file. So I'm guessing it is only reading one line.
int i,origin,destin,wt;
char a, b;
int nodes = 0;
printf("before file is opened\n"); // displayed
FILE *fptr = fopen("input.txt","r");
printf("after file is opened\n"); // displayed
while (fscanf(fptr, "%c %c %d", &a, &b, &wt) == 3){
printf("\before is : %c %c\n", a, b); // shows the first line of file
origin = convertToNum(a);
destin = convertToNum(b);
printf("\nconversion is : %d %d\n", origin, destin); // 1st line of file shown
}
edit
Changing:
while (fscanf(fptr, "%c %c %d", &a, &b, &wt) == 3)
to:
while (fscanf(fptr, "%c %c %d", &a, &b, &wt) != EOF)
Produces slightly better results. It will read in one line just fine.
Every other line seems to have a whitespace issue.
I didn't realize %c also grabbed newline characters.
char buf[512];
while (fgets(buf, sizeof buf, fptr) != 0) {
sscanf(buf, "%c %c %d", &a, &b, &wt);

How to read lines with different formats

I have an input file with the following form
i 176064 Patterson Denise 8.58 11 DEN 15788
q 188464
ra 148702 167443
a 73131
d 163464
f 6.00
ct 73131 PHY
b 3
p 15703
pe
m 144626 6.51 8
e
The first character in each line[i,q,ra,a...] represents an code to a function , while the rest are values that I must store into variables,depending on that code. What's the best way to achieve this ? I have been thinking about using fscanf but each line does not have a specific format, the format itself depends on the code [i,q,ra,a,b..]
To read a line, use fgets()
char buffer[100];
while (fgets, buffer, sizeof buffer, istream) != NULL) {
Then scan the line against the various formats, each ending with " %n". "%n" records the scan position, if it got that far. Additional tests could check for extraneous extras character starting at n.
int num1, num2, num3;
char last[sizeof buf];
char first[sizeof buf];
char code[sizeof buf];
double rate;
int n = 0;
// v..v..v..v...v..v..v spaces optional here
sscanf(buffer, "i %d %s %s %lf %d %s %d %n",
&num1, last, first, &rate, &num2, code, &num3, &n);
if (n) {
Handle_i();
continue;
}
sscanf(buffer, "q %d %n", &num1, &n);
if (n) {
Handle_q();
continue;
}
sscanf(buffer, "ra %d %n", &num1, &num2, &n);
if (n) {
Handle_ra();
continue;
}
sscanf(buffer, "e %n", &n);
if (n) {
Handle_e();
continue;
}
...
fail();
}
As each format begins with a unique letter pattern, the sscanf() will quickly exit on mis-match.
Alternative, code could parse out the initial letters for a slightly more efficient decision tree. Suspect profiling will show little performance difference.
As with any complex format, consider how one would maintain the code and it is bound to evolve.

How to read an unknown quantity of integers from console?

I have entries like these:
0 5 260
1 0 -598
1 5 1508
2 1 -1170
I don't know previously how many (console) inputs I'll get, so I have to read until there are no entries left.
I started with a code like this:
int a, b, c;
while(scanf("%d %d %d", &a, &b, &c)!=EOF){
// do stuff here
}
But it never stops asking for new input.
Then, I saw people in other threads suggesting this:
int a, b, c;
while(scanf("%d %d %d", &a, &b, &c)==1){
// do stuff here
}
In this case, it doesn't even enter the while.
Does anyone know what I'm doing wrong?
An approach: Continue asking for input until the input is closed (EOF) or some problem is encountered. (Invalid line of input)
The below uses fgets() to read a line.
Then, " %n" to detect where scanning stopped. If scanning does not reach %n, n will still have the value of 0. Otherwise it gets the offset in buffer where scanning stopped, hopefully it was at the null character '\0'.
char buffer[100];
while (fgets(buffer, sizeof buffer, stdin)) {
int n = 0;
sscanf(buffer, "%d%d%d %n", &a, &b, &c, &n);
if (n == 0) {
fprintf(stderr, "3 int were not entered\n");
break;
}
if (buffer[n] != 0) {
fprintf(stderr, "Extra input detected.\n");
break;
}
// do stuff here with a,b,c
}
There are many approaches to solve this issue.
while(scanf("%d %d %d", &a, &b, &c)==1)
means that "if scanf() successfully read just one value, proceed in the loop."
Therefore, if you enter something like 0 junk, the scanf() read just 1 data and will enter the loop once.
Try using
while(scanf("%d %d %d", &a, &b, &c)==3)
to have it enter the loop when scanf() successfully read three values, which is what expected.

How to use scanf in for loop

Whenever i try to read input with
for (int i = 1; i <= 10; ++i) {
scanf("(%c, %d, %d, %d)",&charv,&intv1,&intv2,&intv3);
}
I only get to scanf() once. What is the problem ?
Input -> (P, 1, 2, 3)......(P, 2, 3, 12)
Your usage of scanf() is wrong. You have to provide the pointer to the variable to store the value read by scanf(). You need to use it like below
for (int i = 1; i <= 10; ++i) {
scanf("%c, %d, %d, %d",&charVar,&intvar1,&intVar2,&intVar3);
}
EDIT:
Point 1: The supplied format string should exactly match with the input. Otherwise, scanf() will fail. If your input is not of format (<char>, <int>.... , it will fail. Either of missing (, ), , will cause mismatch in the supplied format string with the input and make scanf() to stop scanning. It's strongly recommended to check the return value of scanf() to ensure it's success.
Point 2: To avoid reading the \n stored by previous ENTER<\kbd> key press, you should add a leading space before %c. So, you can use something like
scanf(" %c, %d, %d, %d",&charVar,&intvar1,&intVar2,&intVar3);
^
|
Notice here
scanf("(%c, %d, %d, %d)",&charvar,&intvar1,&intvar2,&intvar3);
should be
scanf(" %c, %d, %d, %d",&charvar,&intvar1,&intvar2,&intvar3);
Note the space before %c which ignores newline if it exists. If your input is not separated by commas
scanf(" %c %d %d %d",&charvar,&intvar1,&intvar2,&intvar3);
Like Sourav Ghosh and Gopi said, scanf will not work properly with this syntax
scanf("(%c, %d, %d, %d)",&char,&int,&int,&int);
It should be
scanf("%c %d %d %d",&char,&int,&int,&int);
But you can read a string first, and then use sscanf.
Try this code:
char ch;
int a, b, c, i;
char teste[256];
for(i=0;i<10;i++){
fgets(teste, 256, stdin);
sscanf(teste, "(%c, %d, %d, %d)", &ch, &a, &b, &c);
printf("%c %d %d %d\n", ch, a, b, c);
}

How to read from .txt in C

I have to read from a .txt like this (1. - txt line)
1 2
1 2 3
1 3 4
but I have to read like: "1" first line, attribute to x[0], "2" first line attribute to x[1]. I know how to do that but the problem is that I have to skip to the line 2 and do the same, but it doesn't work.
So It'd be like
x[2]=1. x[2]=2, x[3]=3, x[4]=1, x[5]=3, x[6]=4
Is there a way for me to do it???
Thanks!
Let me try to be more especific
1 2 1
2 3 1
3 4
Imagine this is a txt file where 3. 2. and 1. are first, second and third line. I have to read each number on each line and assign to a vet[MAX];
I can do it, but only with the first line. I don't know how to skip to the second one
My code
#include <stdlib.h>
int main (void)
{
char buf[1024];
int numeros[8];
FILE *fp = fopen("teste.txt", "r");
if(fp == NULL)
return EXIT_FAILURE;
while(fgets(buf, sizeof(buf), fp)) {
if(buf[0] == '\n')
continue;
sscanf(buf, "%d %d %d %d %d %d %d", &numeros[0], &numeros[1], &numeros[2], &numeros[3],&numeros[4], &numeros[5], &numeros[6]);
}
fclose(fp);
printf(" \n %d \n %d \n %d \n %d \n %d \n %d \n %d", numeros[0], numeros[1], numeros[2], numeros[3],numeros[4], numeros[5], numeros[6]);
}
the output
3
4
1
131072
2685712
302692880
4798692
Process returned 53 (0x35) execution time : 0.016 s
Press any key to continue.
And I wanted
1 2 2 1 2 3 1 3 4
Assuming your double use of x[2] is a typo, and assuming the line numbers are not really in the file, all you need is to loop doing:
fscanf(file, "%d", &x[i++]);
until it fails. So remember the check the return value, if it isn't 1 it failed to find a number to convert and store, and you should stop.
Of course, this assumes that x has space enough, and that i is initialized to 0.
Use the result of sscanf().
You will likely get 2 or 3 each loop. This means 2 or 3 int were successfully scanned. This number can then be used to determine saving the int in numeros[].
int i=0;
while(fgets(buf, sizeof(buf), fp)) {
int t[7];
int result = sscanf(buf, "%d %d %d %d %d %d %d", &t[0], &t[1], &t[2], &t[3], &t[4], &t[5], &t[6]);
if (result <= 0) break; // EOF, IO error, bad data
for (int r=0; r<result; r++) {
if (i >= sizeof(numeros)/sizeof(numeros[0[)) break; // too many
numeros[i++] = t[r];
}
}

Resources