Reputation: 360
I have an input text file which looks something like this:
1(1.230000e+00)
2(1.230000e+00)
(1.230000e+00 1.230000e+00)
3(1.230000e+00)
(1.230000e+00 1.230000e+00)
.
.
.
I want to be able to read each line separately and distinguish between them. For example, for the first line, I want to store 100
in one variable as an int
and I want to store 1.230000e+00
in another variable as a double
. This is what I have tried:
fscanf(fp, "%d(%le)\n", &varInt, &varDouble);
This works for the first line. But how can I loop through and do this for all the lines AND also read the 3rd line using:
fscanf(fp, "(%le %le)\n", &varDouble1, &varDouble2);
To give some context, after reading each line, I will do some processing and then read the next line. Depending on the format of the line, I will do different type of processing.
Any help is appreciated! Thank you!
Upvotes: 1
Views: 619
Reputation: 7837
fscanf(3) is almost unusable unless the input is strictly controlled. It's hard to distinguish between I/O errors and parsing errors. That's why it's much more common to read each line with fgets(3), and then scan it with sscanf(3).
Because sscanf returns the number of elements parsed, you can use that to determine if a scan works as expected. No need to peek at the input: if you got what you expected, you're done, else try scanning some other way. Here's a working example:
#include <assert.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
int
main( int argc, char *argv[] ) {
if( argc < 2 ) {
errx(EXIT_FAILURE, "syntax: %s filename", argv[0]);
}
FILE *input = fopen(argv[1], "r");
if( !input ) {
err(EXIT_FAILURE, "could not open '%s'", argv[0]);
}
static char line[128];
int n;
while( fgets(line, sizeof(line), input) != NULL ) {
double d1, d2;
int quantum;
if( 2 == sscanf(line, "%d(%lf)", &quantum, &d1) ) {
printf( "ok: %d\t%7.2f\n", 100 * quantum, d1 );
} else if( 2 == sscanf(line, "(%lf %lf)", &d1, &d2) ) {
printf( "ok: %7.2f\t%7.2f\n", d1, d2 );
} else {
printf( ">>> %s\n", line );
}
}
if( !feof(input) ) {
err(EXIT_FAILURE, "error reading %s", argv[1]);
}
return EXIT_SUCCESS;
}
If you discover other patterns, it's easy to add them. Note that when fgets fails, the program returns success only if we reached end of file.
Upvotes: 2
Reputation: 6038
As mentioned in the comments, you can read a full line and then determine the format of the line and parse the line accordingly. The following code does what you want. However, production worthy code would probably more robustly interpret the format of each line.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
char *GetStringNoBeginWhitespace( char *str )
{
static const char whitespace[] = " \f\n\r\t\v";
return(str + strspn(str, whitespace));
}
int main(int argc, char *argv[])
{
char *line = NULL;
FILE *fp;
char buffer[255];
int i;
double d,d1;
fp = fopen("data.txt", "r");
while(fgets(buffer, 255, fp))
{
buffer[strlen(buffer)-1] = 0x00;
line = GetStringNoBeginWhitespace( buffer );
if( line )
{
fputs(line, stdout);
if( isdigit((int)line[0] ))
{
printf("\tFormat is x(.......)\n");
if( sscanf(line,"%d(%le)\n", &i, &d) == 2 )
{
printf(" %d %le\n", i, d);
}
else
{
printf("\tUnknown format....\n");
}
}
else if( line[0] == '(' )
{
printf("\tFormat is ( ...... ....... )\n");
if( sscanf(line, "(%le %le)\n", &d, &d1) == 2 )
{
printf(" %le %le\n", d, d1);
}
else
{
printf("\tUnknown format....\n");
}
}
else
{
printf("\tUnknown format....\n");
}
}
}
fclose(fp);
return(0);
}
Output:
jnorton@ubuntu:~/source$ ./a.out 1(1.230000e+00) Format is x(.......) 1 1.230000e+00 2(1.230000e+00) Format is x(.......) 2 1.230000e+00 (1.230000e+00 1.230000e+00) Format is ( ...... ....... ) 1.230000e+00 1.230000e+00 3(1.230000e+00) Format is x(.......) 3 1.230000e+00 (1.230000e+00 1.230000e+00) Format is ( ...... ....... ) 1.230000e+00 1.230000e+00
data.txt file:
1(1.230000e+00) 2(1.230000e+00) (1.230000e+00 1.230000e+00) 3(1.230000e+00) (1.230000e+00 1.230000e+00)
Upvotes: 0
Reputation: 6570
If you generate the file, add fixed size prefix for the length of line. E.g. 016:1(1.230000e+00)\n
. Then read 4 bytes with fread
, convert string to int with strtol
and read rest of line (\n
is included in length). Finally split the values with strtok( str, "( )" )
.
Upvotes: 0