Search code examples
cregexfileio

C read strings from a file with complext format


So I have a file that looks like this:

9
$11$ pelle
$2$ pollo
$333$ palla
$41$ alla
$5078$ tta
$6$ tti
$7$ ll
$81$ er
$900$ ere

n = 9 is the number of key value pairs I want to read in my program.

so for key = "11", value = "pelle" and so on...

typedef struct {
    char key[20];
    char value[20];
} Dictionary;

int main() {
    FILE *fin = fopen("../dizionario.txt", "r");
    if (fin == NULL) {
        printf("error");
        return 1;
    }

    int n;
    fscanf(fin, "%d", &n);

    Dictionary dict[n];
    for (int i = 0; i < n; i++)
        fscanf(fin, "$%[^$]$ %s\n", dict[i].key, dict[i].value);
    
    fclose(fin);

    for (int i = 0; i < n; i++) {
        printf("Key: %s, Value: %s\n", dict[i].key, dict[i].value);
    }

    return 0;
}

I have adopted the following approach to read each key-value pair and store them in a dict struct which I believe to be a decent choice. When I print to the console to check the values are all over the place, the output being:

Key: , Value:
Key: , Value:
Key: , Value:
Key: , Value:
Key: , Value:
Key: , Value:
Key: , Value: å
Key: <0¶[÷, Value:
Key:    , Value: °

I believe the problem to with the format string which I can't seem to get right:

"$%[^$]$ %s\n"

Solution

    1. The easiest fix here is add a whitespace before the 2nd format string so it ignores the trailing newline from the preceding line.
    2. Use symbolic constants for your size, and always use maximum field with values when reading strings with scanf().
    3. Consider adding a check on n to ensure it's sufficiently small (on my system the default stack is 8 MB; or even better use malloc() and check that it was successful).
    #include <stdio.h>
    
    #define KEY_LEN 19
    #define VALUE_LEN 19
    #define str(s) str2(s)
    #define str2(s) #s
    
    typedef struct {
        char key[KEY_LEN + 1];
        char value[VALUE_LEN + 1];
    } Dictionary;
    
    int main() {
        FILE *fin = fopen("../dizionario.txt", "r");
        if (!fin) {
            printf("error");
            return 1;
        }
        int n;
        fscanf(fin, "%d", &n);
        Dictionary dict[n];
        for (int i = 0; i < n; i++)
            fscanf(fin, " $%" str(KEY_LEN) "[^$]$ %" str(VALUE_LEN) "s", dict[i].key, dict[i].value);
        fclose(fin);
    
        for (int i = 0; i < n; i++)
            printf("Key: %s, Value: %s\n", dict[i].key, dict[i].value);
    }
    

    and corresponding output:

    Key: 11, Value: pelle
    Key: 2, Value: pollo
    Key: 333, Value: palla
    Key: 41, Value: alla
    Key: 5078, Value: tta
    Key: 6, Value: tti
    Key: 7, Value: ll
    Key: 81, Value: er