问题
I'm trying to use fscanf
to read in data, and part of the input is a float followed by the letter 'e'
, for example, 41.72elapsed
. When writing the strng for fscanf
, I attempted to use "%felapsed"
, but this doesn't work, as %fe
is its own format specifier. How would I read this in using fscanf
?
edit: Here is the code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define CHAR_MAX 1024
int main(int argc, char **argv)
{
FILE *file_in = fopen(argv[1], "r+");
char out_name[CHAR_MAX];
strcpy(out_name, argv[1]);
strcat(out_name, ".csv");
FILE *csv_out = fopen(out_name, "w");
int minutes;
float seconds;
fprintf(csv_out, "Trial #, Execution Time\n");
for (int i = 0; fscanf(file_in, "%*fuser %*fsystem %d:%felapsed %*d%%CPU (%*davgtest+%*davgdata %*dmaxresident)k\n%*dinputs+%*doutputs (%*dmajor+%*dminor)pagefaults %*dswaps\n", &minutes, &seconds) == 2; i++) {
fprintf(csv_out, "%d, %d:%.2f\n", i, minutes, seconds);
};
return 0;
}
Here is some sample input:
283.97user 0.69system 1:13.77elapsed 385%CPU (0avgtext+0avgdata 107472maxresident)k
0inputs+4616outputs (0major+9550minor)pagefaults 0swaps
287.87user 0.35system 1:14.41elapsed 387%CPU (0avgtext+0avgdata 107328maxresident)k
0inputs+4616outputs (0major+9524minor)pagefaults 0swaps
回答1:
This is a problem with scanf()
FP formats like "%f"
see the e
as introducing exponentiation. Since the e
is not followed by a number, scanning for the float
stops. But scanf()
has all ready scanned one past the e
and C does not require for scanf()
to be able to back up more than 1 character. So code is out-of-luck using a simple "%f"
.
Some systems will back up more than 1 character, but C does not require that capability.
Code needs a new approach - scan in seconds as a string
char sec[20];
int cnt = fscanf(file_in, "%d:%19[0-9. ]elapsed", &minutes, sec);
if (cnt == 2) {
seconds = atof(sec);
...
}
回答2:
There's simply no need for the "elapsed"
in your format. The scanf family of function will read as long as they can from the input, and for floating-point number it will stop reading when it hits a non-digit character, i.e. the e
in elapsed
.
So the format only needs to be "%f"
, and that's it. I.e.
float value;
fscanf(file, "%f", &value);
If you want to read and discard the elapsed
part, use "%*s"
, the asterisk tells scanf (and family) to read and ignore the rest, so the full call would look like
float value;
fscanf(file, "%f%*s", &value);
After seeing your code, it can be much simpler and easier with something like
char input[512];
for (int i = 0; fgets(input, sizeof input, file_in) != NULL; ++i) {
if (sscanf(input, "%*f%*s %*f%*s %d:%f%*s", &minutes, &seconds) == 2) {
fprintf(csv_out, "%d, %d:%.2f\n", i, minutes, seconds);
}
}
Since this loop uses fgets
instead of direct fscanf
you will read all lines in the input file, not only just the first one. Also since fgets
is used we don't need the sscanf
function to actually parse the parts of the string we don't need (which is most of it), instead we only have sscanf
parse the input string until we have the data we need.
回答3:
This is a bit of a hack and may be too brittle, but:
The float you want to parse seems to be a time in minute.second format, with positive integers. If the producer of the data reliably pads small numbers with zero (e.g. 1:02.03), you can simply use a fixed field length of 5, because seconds and minutes will never be larger than 59 and thus always be two characters wide each:
sscanf("12.345678", "%5f%s, &f, buf)will read 12.34 into f and 5678 into buf. (The same, of course, with "12.34elapsed". I just wanted to make it unmistakably clear that only 5 characters of the input are consumed.)
回答4:
Let's do an experiment:
#include <stdio.h>
int main (void)
{
float fp;
scanf("%f", &fp);
printf("%f", fp);
}
Input: 123e4
Output: 1230000.000000
As you can see, 'e' is considered as part of floating-point number specified by "%f".
For me, the simplest solution is to use scanf("%f%*s ", &f);
. After rejected by "%f", "lapsed" is consumed by "%*s", without causing problems. When it comes to 'e', it's just discarded, because the C spec has a footnote "fscanf pushes back at most one input character onto the input stream."
BTW: Do you have to process the floating-point numbers? If not, what about simply treating them as strings? For example, scanf("%[^e]elapsed", str);
?
来源:https://stackoverflow.com/questions/36086616/how-to-scanf-a-float-followed-immediately-by-the-letter-e-in-c