How to scanf a float followed immediately by the letter 'e' in c?

爱⌒轻易说出口 提交于 2021-02-04 22:06:36

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!