how to read multiple variables separated by commas using only C fscanf() - c

Say Given a text file that looks like this:
a,b,c
x,y,z
where a is a char *, b contains a float and c contains a double.
For an example, the input file can look like this:
apple,$12.34,test130.8
x,y,z
I want to use fscanf() to read a, b, c and assign each one of them to a corresponding variable.
"apple" will be assigned to A of the same data type; "12.34"(not "$12.34") will be assigned to B with a float data type; so on.
My attempt was as follows:
fp = the file pointer
char A[50];
float B;
double C;
fscanf(fp, "%[^,],%[^,],%[^,]\n", A, B, C);
But I realized that %[^,]can only specify type char *; ergo, I'm not allowed to assign type char * to a float or double variable.
Is there a way to parse %[^,] to make it only specifies type float?
if I only use this:
fscanf(fp, "%s,%f,%lf\n", A, B, C);
It will be thrown off by the "$" in "12.34", and it will give me 0.000000.

Using sscanf() (instead of fscanf()) for ease of testing:
#include <stdio.h>
#include <stdlib.h>
int main() {
char *s = "apple,$12.34,test130.8\npear,$23.45,abc";
for(int offset = 0, n;; offset += n) {
char *symbol;
float price;
char *note;
if(sscanf(s + offset, " %m[^,],$%f,%m[^\n]%n", &symbol, &price, &note, &n) != 3) {
break;
}
printf("symbol: %s, price: %f, note: %s\n", symbol, price, note);
free(note);
free(symbol);
}
}
and the matching output (note how it demonstrate the evils of using floating points for money):
symbol: apple, price: 12.340000, note: test130.8
symbol: pear, price: 23.450001, note: abc
I used %m to have scanf() allocate the strings. If I knew the maximum size of the strings I would reuse a fixed size strings instead of dynamically allocating and freeing those.
When using fscanf() instead of break you could use feof() to see if we are done, or if the input is invalid. If it's invalid you may want to resync to the next \n with fsccnf(..., "%c", ch). For the above s[offset] == '\0' will tell if you are the end but see below.
You may find it's much easier to get a line with fgets(), then use sscanf() similar to above to extract each item. If fails you can report the line and just read the next one. fgets() will return NULL if you have no more data and it leads to cleaner code when you separate I/O and parsing.

There's already an answer from #AllanWind (using dynamic allocation for strings that my old library doesn't do.) Here's an alternative solution (that is much the same.)
First, the input file used for testing:
apple,$12.34,test130.8
banana,$20.67,testing201.45
Then the code using fscanf() with a complicated format string:
int main( void ) {
FILE *fp = fopen( "test.txt", "r" );
if( fp == NULL) {
fprintf( stderr, "fopen() failed\n" );
return -1;
}
char txt[50], word[12];
double dval1, dval2;
while( fscanf( fp, " %49[^,],%*c%lf,%11[^0123456789]%lf", txt, &dval1, word, &dval2 ) == 4 )
printf( "'%s' / %.2lf / '%s' / %.2lf\n", txt, dval1, word, dval2 );
fclose( fp );
return 0;
}
Finally, the output
'apple' / 12.34 / 'test' / 130.80
'banana' / 20.67 / 'testing' / 201.45

Related

Can I use a string in C and get two different data values from it?

Is it possible to take a string and get two different values from it to store in different type variables, more specifically, an integer and a character.
Brief example:
string : "A - 3"
int number : 3
char name : 'A'
I've seen people take multiple int from a string but nothing worked for me. I should add I am very new to C, so I don't really know how to mess around with it too much.
Its for a slide puzzle minigame where I need to read a text file with all instructions from the user, formatted with what direction each number block goes ("8 r" = 8 goes right), and print to the terminal each and every move. If anyone could suggest how I could take an int and char at the same time from a line on a file, I would also be grateful.
You can use fscanf(), defined in stdio.h, to read from a file:
int fscanf (FILE *stream, const char *format-string, argument-list)
stream is a file you previously opened. fscanf() returns the number of fields that it successfully converted and assigned. The return value does not include fields that the fscanf() function read but did not assign.
Example:
#include <stdio.h>
int main()
{
FILE* f = fopen("file.txt", "r");
int value;
char ch;
if (fscanf(f, "%d - %c", &value, &ch) != 2)
printf("Error");
printf("value: %d\nchar: %c\n", value, ch);
fclose(f);
return 0;
}
File file.txt content:
100 - A
Output:
value: 100
char: A
If your purpose is to parse a string, the simplest way to achieve this is to use strtok() defined in string.h header:
char *strtok(char *string1, const char *string2);

How to read comma delimited data from txt file to struct

I have text file which have the following content
inputfile
I have used a function to get data from the input file which is comma delimited.
I want to read data from it and want to remove comma and store the data to Struct Resistor_struct.
I have tried the following code.
'''
#include<stdio.h>
//functions header
int blown_ressistors();
struct resistor_struct
{
char ID_LEN[5];
char id;
float max_poewr;
int resistance;
};
struct resistor_struct rs[100];
int blown_ressistors()
{
FILE *fp = fopen("input.txt", "r");
int i = 0;
if(fp!=NULL)
{
while(fscanf(fp, "%s[^,], %d[^,], %f[^,]",rs[i].ID_LEN,rs[i].resistance, rs[i].max_poewr)!=EOF)
{
printf("%s\t", rs[i].ID_LEN);
printf("%d\t", rs[i].resistance);
printf("%d\t\n", rs[i].max_poewr);
i++;
}
}
else
{
perror("Input.txt: ");
}
'''
output
output image
You don't want to compare the value returned from scanf with EOF.
In your case, the format string is such that scanf can never match more than 1 conversion specifier, since %s[^,], is trying to match the literal input string [^,], but the [ is guaranteed not to match since the first character that scanf will stop consuming for the %s is whitespace. And [ is not whitespace. Try something like:
while(fscanf(fp, " %4[^,], %d, %f", rs[i].ID_LEN, &rs[i].resistance, &rs[i].max_poewr) == 3 )
but note that this will behave oddly on whitespace in the first column. You might want to try: " %4[^, \t\n] , %d, %f", but quite frankly the better solution is to stop using scanf. Even with something trivial like this, your behavior will be undefined on an input like foo, 9999...9999 (where the 2nd column is any value that exceeds the capacity of an int). Just stop using scanf. Read the data and parse it with strtol and strtod.

Using fgets for file read

I'm new to using strings in C and am needing to read from a file lines of data that contain strings and numbers, parsing them as I go along. I've done similar programs reading in just numbers, such as a list of ordered pairs, using a for loop so this is the strategy I am leaning towards.
Example of data line in the file: PART,2.000,-1,0.050,V
When I compile I get an error in the for loop declaration of "expected expression before 'char'". What is missing or needs reviewing in this code?
#define flush fflush(stdin)
#define N 50
int main()
{
flush;
const char part[] = "PART"; // String for PART variable
char descriptor[N]; // Starting string of data set
double p_dim[N]; // Array for part dimensions
int t_sens[N]; // Array for sensitivity values: -1 or +1
double t[N]; // Array for part tolerance, bilateral
char t_status[N]; // Array for tolerance status, F(ixed) or V(ariable)
double key_max; // Maximum value of key characteristic
double key_min; // Minimum value of key characteristic
FILE* fpin;
if((fpin = fopen("input.txt","r"))==(FILE*)NULL)
{
printf("File input does not exist\n"); exit(-1);
}
// For loop to parse data lines from file
for(N; char* fgets(descriptor, int N, FILE* fpin); N-1);
{
compare(descriptor, part);
if (descriptor == part)
{
fscanf(fpin, "%lf,%d,%lf,%s", p_dim[N], t_sens[N], t[N], t_status[N]);
}
else if (descriptor != part)
{
fscanf(fpin, "%lf, %lf", &key_min, &key_max);
}
}
1.) #define flush fflush(stdin)
Flushing stdin invokes undefined behaviour.
2.) if((fpin = fopen("input.txt","r"))==(FILE*)NULL)
The cast to (FILE*) is superfluous.
3.) for(N; ... ; N-1);
You defined N as a constant (#define N 50) so this loop won't ever exit.
4.) for(... ; char* fgets(descriptor, int N, FILE* fpin); ...);
This is just plain wrong ...
I'd lean more toward breaking the string apart
See question 3501338 for reading a file line by line
See question 15472299 using strtok to break apart the string
If you need to cast the strings as numbers use sscanf

C fscanf returning the wrong value

I'm really stuck on something.
I have a text file, which has 1 word followed by ~100 float numbers. The float numbers are separated by space, tab, or newline. This format repeats several times throughout the text file.
For example, this is what the text file looks like:
one 0.00591 0.07272 -0.78274 ...
0.0673 ...
0.0897 ...
two 0.0654 ...
0.07843 ...
0.0873 ...
three ...
...
...
This is a snippet of my code:
char word[30];
double a[1000];
double j;
while (!feof(fp))
{
fscanf(fp, "%s", word);
printf("%s\n", word);
while (!feof(fp) && (fscanf(fp, " %lf", &j)) == 1)
{
a[z] = j;
z++;
num_of_vectors++;
}
z = 0;
}
The word "nine" in the text file, is printed as "ne".
And the word "in" doesn't even print, a floating point number gets printed.
What am I doing wrong?
Any help would be much appreciated.
Thanks.
As per the standard:
An input item is defined as the longest sequence of input characters which does not exceed any specified field width and which is, or is a prefix of, a matching input sequence.
The likely reason that nine is giving you ne is because, when reading a double value, nan is one of the acceptable values. Hence, the n and i are read to establish that it's not nan.
Similarly, with the word in, that a valid prefix for inf representing infinity.
The standard also states in a footnote:
fscanf pushes back at most one input character onto the input stream.
so it's quite possible that this is why the i in nine is not being pushed back.
Bottom line is that it's basically unsafe to assume where the file pointer will end up when fscanf operations fail for some reason.
One way to fix this is to use ftell and fseek to save the file pointer for each successfully item, so that you can move back to the correct file position if the thing you're attempting to read is not successful.
Let's say you have the input file:
one 1 2 3 4 5
nine 9 8 7 6 5
in 3.14159 2.71828
The following code will save and restore file positions to make it work as you wish:
#include <stdio.h>
int main(void) {
char buff[50]; double dbl; size_t pos;
FILE *fin = fopen("inputFile.txt", "r");
while (fscanf(fin, "%s", buff) == 1) {
printf("Got string [%s]\n", buff);
pos = ftell(fin);
while (sscanf(buff, "%lf", &dbl) == 1) {
printf("Got double [%f]\n", dbl);
pos = ftell(fin);
}
fseek(fin, pos, SEEK_SET);
}
fclose(fin);
return 0;
}
By commenting out the fseek, you can see similar behaviour to what you describe:
Got string [one]
Got double [1.000000]
Got double [2.000000]
Got double [3.000000]
Got double [4.000000]
Got double [5.000000]
Got string [ne]
Got double [9.000000]
Got double [8.000000]
Got double [7.000000]
Got double [6.000000]
Got double [5.000000]
Got double [3.141590]
Got double [2.718280]
I consider this solution a little messy in that it's continuously having to call ftell and occasionally fseek to get it to work.
Another way is to just read everything as strings and decide whether it's a numeric or string with a sscanf operation after reading it in, as in the following code (with the afore-mentioned input file):
#include <stdio.h>
int main(void) {
char buff[50]; double dbl;
FILE *fin = fopen("inputFile.txt", "r");
while (fscanf(fin, "%s", buff) == 1) {
if (sscanf(buff, "%lf", &dbl) == 1) {
printf("Got double [%f]\n", dbl);
} else {
printf("Got string [%s]\n", buff);
}
}
fclose(fin);
return 0;
}
This works because a floating point value is actually a proper subset of a string (i.e., it has no embedded spaces).
The output of both those programs above is:
Got string [one]
Got double [1.000000]
Got double [2.000000]
Got double [3.000000]
Got double [4.000000]
Got double [5.000000]
Got string [nine]
Got double [9.000000]
Got double [8.000000]
Got double [7.000000]
Got double [6.000000]
Got double [5.000000]
Got string [in]
Got double [3.141590]
Got double [2.718280]
which is basically what was desired.
One thing you need to be aware of is that scanning something like inf or nan as a double will actually work - that is the intended behaviour of the library (and how your original code would have worked had it not had the issues). If that's not acceptable, you can do something like evaluate the string before trying to scan it as a double, to ensure it's not one of those special values.

C using scanf() for | delimited string

I want to input a few strings then two integers. Whilst the strings are separated by '|', the integers are kept apart by a '.'.
Looking around online I have seen some sort of syntax which involves [^]. I am using this but it is not working at all. Can someone please point out what I should be doing and why what I am doing is wrong?
sscanf(str, "%s[^|],%s[^|],%s[^|],%i[^|],%i[^.]", …);
The syntax is arcane at best — I'd suggest using a different approach such as strtok(), or parsing with string handling functions strchr() etc.
However the first thing you must realise is that the %[^<delimiter-list>] format specifier (a 'scan set' in the jargon, documented by POSIX scanf()
amongst many other places) only extracts string fields — you have to convert the extracted strings to integer if that is what they represent.
Secondly you still have to include the delimiter as a literal match character outside of the format specifier — you have separated the format specifiers with commas where | are in the input stream.
Consider the following:
#include <stdio.h>
int main()
{
char a[32] ;
char b[32] ;
char c[32] ;
char istr[32] ; // Buffer for string representation of i
int i ;
int j ; // j can be converted directly as it is at the end.
// Example string
char str[] = "fieldA|fieldB|fieldC|15.27" ;
int converted = sscanf( str, "%[^|]|%[^|]|%[^|]|%[^.].%i", a, b, c, istr, &j ) ;
// Check istr[] has a field before converting
if( converted == 5 )
{
sscanf( istr, "%i", &i) ;
printf( "%s, %s %s, %d, %d\n", a, b, c, i, j ) ;
}
else
{
printf( "Fail - %d fields converted\n", converted ) ;
}
return 0 ;
}
You must use either [] or s construct, but not both and your format string must incluse the separators.
So you should write something like :
sscanf(str, "%[^|]|%[^|]|...",...)
This seems to work...
#include <stdio.h>
main()
{
char x[32] = "abc|def|123.456.";
char y[20];
char z[20];
int i =0;
int j =0;
sscanf(x,"%[^|]|%[^|]|%d.%d.",y,z,&i,&j);
fprintf(stdout,"1:%s 2:%s 3:%d 4:%d\n",y,z,i,j);
}

Resources