Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
501 views
in Technique[技术] by (71.8m points)

c - Saving from a linked list to a file and loading it back

I'm having trouble loading from a file into a linked list, been trying the whole day

first of all this is my struct

typedef struct Sensor {
    int id;
    int intervalo;
    char local[30];
    char tipo[30];
    //bool active;
    int active;
    struct Sensor* anterior;
    struct Sensor* proximo;
} Sensor;

this is my save function which i think its working fine, since the file gets created and the content is there.

void gravaLista(Sensor* l) {
    FILE *ficheiro;
    Sensor* temp = l;
    ficheiro = fopen("sensores.txt", "r+t");

    if (ficheiro == NULL) {
        ficheiro = fopen("sensores.txt", "w+t");
    }

    while (temp != NULL) {
        fprintf(ficheiro, "%d%d%d%30s%30s", temp->id, temp->intervalo, temp->active,
        temp->local, temp->tipo);
        temp = temp->proximo;
    }

    fclose(ficheiro);
}

now where i cant seem to make this work regardless of what i read about it is the load function.

heres what i have atm

int CarregaTodos(Sensor** l) {
    Sensor sens;
    FILE *ficheiro;
    int i = 0;

    ficheiro = fopen("sensores.txt", "r+t");

    if (ficheiro == NULL) {
        printf("no file
", "sensores.txt");
        return i;
    }

    rewind(ficheiro);
    while (fscanf(ficheiro, "%d%d%d%30s%30s", &sens.id, &sens.intervalo, &sens.active,
            &sens.local, &sens.tipo) == 5) {

            //novo() function returns a pointer to a new element and insereSensor adds the new element to the last position of the list
            insereSensorFim(&l, novo(sens.id, sens.intervalo, sens.local, sens.tipo));                              //this function inserts the new element at the end of the list
    }

    fclose(ficheiro);
    return i;
}

the helper functions work fine outside of the load function, but when i try to print the list after loading nothing gets printed. what am i missing?

edit: ill just post the helper functions too

Sensor* novo(int id, int tempo, char* l, char* t) {

    Sensor* novoSensor = (Sensor*)malloc(sizeof(struct Sensor));

    //novoSensor->id = ++(*totalSens);
    novoSensor->id = id;
    novoSensor->intervalo = tempo;
    strcpy(novoSensor->local, l);
    strcpy(novoSensor->tipo, t);
    novoSensor->active = 1;
    novoSensor->anterior = NULL;
    novoSensor->proximo = NULL;

    //gravaSensor(novoSensor, (*totalSens), 1);

    return novoSensor;
}

void insereSensorFim(Sensor** Lista, Sensor* novo) {

    Sensor* atual = *Lista;

    if ((*Lista == NULL))
        (*Lista = novo);

    else {

        while (atual->proximo != NULL) {
            atual = atual->proximo;
        }

        atual->proximo = novo;
        novo->anterior = atual;
    }
}

edit2: its fixed now, thanks to everyone who commented, you can read all the comments or just https://stackoverflow.com/a/44078897/8038340

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Using printf() and scanf() properly is surprisingly hard. It's possible to do all sorts of magic with them, but you need to know how they work to be able to perform that magic.

In the example code, you make life more difficult for yourself by not including a record delimiter in the output. A newline is the conventional and simplest delimiter, but you can choose others if you wish, or no delimiter. However, if you choose no delimiter, you have to know information about the data that is not given in the question. If the strings never contain spaces, you can be less stringent in your formatting. But you must have some way of knowing where one number ends and the next one starts — you can't simply smush all the numbers together as the sample printf() format does unless they're all negative, or you add a plus sign to the positive number (%+d). There has to be some way to tell scanf() when to stop reading one and start on the next number.

This code is an elaboration of what I wrote in numerous comments. The output format uses fixed width fields; this makes it easier to read them. It does not assume there are no spaces in the strings, so it uses %29c to read 29 characters, and adds a null-terminator and removes trailing blanks via strip_blanks(). It includes code to print lists; it uses that code.

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

typedef struct Sensor
{
    int id;
    int intervalo;
    char local[30];
    char tipo[30];
    int active;
    struct Sensor *anterior;
    struct Sensor *proximo;
} Sensor;

static void insereSensorFim(Sensor **Lista, Sensor *novo);
static Sensor *novoSensor(int id, int tempo, char *l, char *t);

static const char *outfile = "sensores.txt";

static
void gravaLista(Sensor *l)
{
    FILE *ficheiro = fopen(outfile, "w");
    if (ficheiro == NULL)
    {
        fprintf(stderr, "Failed to open file '%s' for writing
", outfile);
        exit(1);
    }

    Sensor *temp = l;
    while (temp != NULL)
    {
        fprintf(ficheiro, "%11d%11d%11d%-29.29s%-29.29s", temp->id, temp->intervalo, temp->active,
                temp->local, temp->tipo);
        temp = temp->proximo;
    }

    fclose(ficheiro);
}

/* Strip trailing blanks and null terminate string */
static inline void strip_blanks(char *data, size_t size)
{
    assert(size > 0);
    size_t offset = size - 1;
    data[offset--] = '';
    while (offset > 0 && data[offset] == ' ')
        data[offset--] = '';
}

static
int CarregaTodos(Sensor **l)
{
    Sensor sens;
    FILE *ficheiro;
    int i = 0;

    ficheiro = fopen(outfile, "rt");

    if (ficheiro == NULL)
    {
        fprintf(stderr, "Failed to open file '%s'
", outfile);
        exit(1);
    }

    while (fscanf(ficheiro, "%11d%11d%11d%29c%29c", &sens.id, &sens.intervalo, &sens.active,
                  sens.local, sens.tipo) == 5)
    {
        strip_blanks(sens.local, sizeof(sens.local));
        strip_blanks(sens.tipo, sizeof(sens.tipo));
        insereSensorFim(l, novoSensor(sens.id, sens.intervalo, sens.local, sens.tipo));
    }

    fclose(ficheiro);
    return i;
}

static inline void str_copy(char *dst, const char *src, size_t size)
{
    assert(size > 0);
    strncpy(dst, src, size - 1);
    dst[size - 1] = '';
}

static
Sensor *novoSensor(int id, int tempo, char *l, char *t)
{
    Sensor *novoSensor = (Sensor *)malloc(sizeof(struct Sensor));
    if (novoSensor == NULL)
    {
        fprintf(stderr, "Failed to allocate %zu bytes memory
", sizeof(struct Sensor));
        exit(1);
    }

    novoSensor->id = id;
    novoSensor->intervalo = tempo;
    str_copy(novoSensor->local, l, sizeof(novoSensor->local));
    str_copy(novoSensor->tipo, t, sizeof(novoSensor->tipo));
    novoSensor->active = 1;
    novoSensor->anterior = NULL;
    novoSensor->proximo = NULL;

    return novoSensor;
}

static
void insereSensorFim(Sensor **Lista, Sensor *novo)
{
    Sensor *atual = *Lista;

    if ((*Lista == NULL))
        *Lista = novo;
    else
    {
        while (atual->proximo != NULL)
            atual = atual->proximo;
        atual->proximo = novo;
        novo->anterior = atual;
    }
}

static void print_sensor(Sensor *sensor)
{
    printf("%5d %5d %1d [%-29s] [%-29s]
", sensor->id, sensor->intervalo,
           sensor->active, sensor->local, sensor->tipo);
}

static void print_sensor_list(const char *tag, Sensor *list)
{
    printf("%s:
", tag);
    while (list != 0)
    {
        print_sensor(list);
        list = list->proximo;
    }
}

static void free_sensor_list(Sensor *list)
{
    while (list != 0)
    {
        Sensor *next = list->proximo;
        free(list);
        list = next;
    }
}

int main(void)
{
    Sensor *list = 0;

    print_sensor_list("Empty", list);

    insereSensorFim(&list, novoSensor(10231, 23, "abc123-bothersome",  "d92-x41-ccj-92436x"));
    insereSensorFim(&list, novoSensor(20920, 25, "def456-troublesome", "e81-p42-ggk-81366x"));
    insereSensorFim(&list, novoSensor(30476, 83, "ghi789-wearisome",   "f70-q43-omm-70296x"));

    print_sensor_list("After insertion", list);
    gravaLista(list);
    free_sensor_list(list);
    list = 0;
    print_sensor_list("Emptied", list);

    CarregaTodos(&list);
    print_sensor_list("After rereading", list);

    insereSensorFim(&list, novoSensor(231,  325, "jkl012 blank laden stream",       "minimum mess or cleaning"));
    insereSensorFim(&list, novoSensor(6812, -11, "mno345 longer than was expected", "maximum type of untidiness at work"));
    print_sensor_list("After extending", list);

    free_sensor_list(list);

    return 0;
}

When run, it produces the output:

Empty:
After insertion:
10231    23 1 [abc123-bothersome            ] [d92-x41-ccj-92436x           ]
20920    25 1 [def456-troublesome           ] [e81-p42-ggk-81366x           ]
30476    83 1 [ghi789-wearisome             ] [f70-q43-omm-70296x           ]
Emptied:
After rereading:
10231    23 1 [abc123-bothersome            ] [d92-x41-ccj-92436x           ]
20920    25 1 [def456-troublesome           ] [e81-p42-ggk-81366x           ]
30476    83 1 [ghi789-wearisome             ] [f70-q43-omm-70296x           ]
After extending:
10231    23 1 [abc123-bothersome            ] [d92-x41-ccj-92436x           ]
20920    25 1 [def456-troublesome           ] [e81-p42-ggk-81366x           ]
30476    83 1 [ghi789-wearisome             ] [f70-q43-omm-70296x           ]
  231   325 1 [jkl012 blank laden stream    ] [minimum mess or cleaning     ]
 6812   -11 1 [mno345 longer than was expect] [maximum type of untidiness at]

The output file, sensores.txt, looks like this:

      10231         23          1abc123-bothersome            d92-x41-ccj-92436x                 20920         25          1def456-troublesome           e81-p42-ggk-81366x                 30476         83          1ghi789-wearisome             f70-q43-omm-70296x           

When split into records, that is:

      10231         23          1abc123-bothersome            d92-x41-ccj-92436x           
      20920         25          1def456-troublesome           e81-p42-ggk-81366x           
      30476         83          1ghi789-wearisome             f70-q43-omm-70296x           

The integer width of 11 allows for a negative 32-bit number in each of the first two columns. If you know that the numbers are smaller, you can reduce the space used. In the scanf(), you could omit the lengths on the integer fields; it would work the same because numeric formats automatically skip white space. The printf() could add newlines; the scanning code needn't change at all because scanf() doesn't care about newlines when it is expecting a number (or a string — only %c, %[…] scan sets, and %n do not skip leading white space).

You could also arrange for some character that won't appear in the character strings (perhaps Control-A, '1') to separate the strings. Then the scanning code could look for that and you could have variable length output.

Left to my own devices, I'd probably use a variable-length record with newline for the record delimiter, and a suitable field separator for the two strings, and a less rigid scanf() format. I'd read the lines with fgets() or POSIX getline() and then scan the lines using sscanf(). This would work nicely unless you can have newlines in your strings.

As I put it recently in another answer — lightly paraphrased:

Read the POSIX specification of printf() and scanf() for the full details. They do have some (clearly marked) extensions over standard C printf() and scanf(), but they serve for both POSIX and standard C. Then re-read them. And re-re-read them. And do that daily for a week, and then weekly for a month, and then monthly for a year, and then yearly ever after. It will repay the effort.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...