简体   繁体   中英

How to separate numbers from strings in txt file in C?

txt file:

44.56 john doe  
100.21 jane doe

How to calculate sum of numbers? This function give 0

double get_sum(FILE *out)
{
    double sum = 0;
    double value;
    char name;
    char surname;

    while(fscanf(out, "%lf %s %s", &value, name, surname) != EOF)
    {
        sum += value;
    }
    return sum;
}

Just tell fscanf to ignore the name and surname slots like this:

double get_sum(FILE *out) {
    double sum = 0;
    double value;

    while (fscanf(out, "%lf%*s%*s", &value) != EOF) {
        sum += value;
    }
    return sum;
}

The trouble is that you where passing pointers to char instead of a pointer to a char array that can hold the content. Since you didn't it overflowed and caused undefined behavior.

But if you really wanted to also read in the names then try:

double get_sum(FILE *out) {
    double sum = 0;
    double value;
    char name[5];
    char surname[4];

    while (fscanf(out, "%lf%s%s", &value, name, surname) != EOF) {
        sum += value;
    }
    return sum;
}

Here the buffers are just long enough to accommodate the example text files data. In real life (if you needed it) you would have the big enough to handle the longest name. The important difference to your original code is that name and surname are pointers that the fscanf function expects.

But given that names sometimes are unpredictable in length, after reading the value on the line, just read the remaining line into a buffer and just ignore it.

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

double get_sum(FILE *in) {
    double sum = 0;
    double value;
    char *remainingLine = NULL;
    size_t bufLen = 0;

    while (fscanf(in, "%lf", &value) == 1) {
        sum += value;
        // ignore remaining character in line
        getline(&remainingLine, &bufLen, in);
    }
    free(remainingLine);
    return sum;
}

int main(void) {
    FILE *f = fopen("text.txt", "r");
    assert(f);
    double s = get_sum(f);
    fclose(f);
    printf("Sum is %f", s);
}

with text.txt file containing

1.03 First Surname
2.2Here is another long "name" (sum should be 10.63)
3.4 first middle last (next line is lonely and last)
4

Running the this last program should produce simething along the lines of

Sum is 10.630000

In the line

while(fscanf(out, "%lf %s %s", &value, name, surname) != EOF)

it is not good to compare the return value of the function fscanf with EOF . For example, if the function is only able to match one argument, then it will return 1 , but your program will still behave as if all 3 arguments had been matched, and your program will attempt to process non-existant data. Therefore, you should write the following instead:

while( fscanf(out, "%lf %s %s", &value, name, surname) == 3 )

However, that is probably not the reason for the immediate problem that you are having. That problem is probably due to the fact that the %s conversion format specifier requires a pointer to write to. However, you are instead passing the value of (not a pointer to) a single char .

Your compiler should have warned you about this, assuming that you have warnings enabled. See this question for further information: Why should I always enable compiler warnings?

In order to solve this problem, you should change the lines

char name;
char surname;

to:

char name[50];
char surname[50];

Also, you should probably limit the number of characters written to these strings, in order to prevent a buffer overflow , like this:

while( fscanf(out, "%lf %49s %49s", &value, name, surname) == 3 )

For line-based input, I generally recommend that you read one line at a time with the function fgets . You can then use the function sscanf on every line, in order to parse it.

If you use instead fscanf as you do now, and if it does not match exactly 3 fields in one line, then the parser will go out of sync with the lines, and it won't be able to resync itself at the start of a new line, which will probably mean that your program will misbehave for the rest of the file, instead of only misbehaving for a single line.

Here is a program which uses fgets and sscanf instead:

#include <stdio.h>

double get_sum( FILE *fp )
{
    char line[200];
    double sum = 0;

    while ( fgets( line, sizeof line, fp ) != NULL )
    {
        double value;
        char name[50];
        char surname[50];

        if ( sscanf( line, "%lf %49s %49s", &value, name, surname ) == 3 )
        {
            sum += value;
        }
        else
        {
            printf( "WARNING: skipping line due to parse failure!\n" );
        }
    }

    return sum;
}

int main( void )
{
    //calling this function would also work for an opened file, but
    //for simplicity, I am only passing it "stdin"
    double sum = get_sum( stdin );

    printf( "The sum is: %lf\n", sum );
}

With the input

44.56 john doe
100.21 jane doe

from the question, this program has the following output:

The sum is: 144.770000

If you now instead supply the program with input that contains one line of invalid input

44.56 john doe
invalid_input_line
100.21 jane doe

it will only fail with parsing that invalid line, but will still process the other lines properly:

WARNING: skipping line due to parse failure!
The sum is: 144.770000

As already explained, your program is able to recover from this error because it is reading one line at a time using fgets , and it is using sscanf instead of fscanf . Otherwise, recovering from such an error would be much more complicated.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM