从链接列表保存到文件并重新加载 [英] Saving from a linked list to a file and loading it back

查看:90
本文介绍了从链接列表保存到文件并重新加载的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我整天都在尝试从文件加载到链接列表时遇到麻烦

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

首先这是我的结构

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.

这是我拥有的atm

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

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

    if (ficheiro == NULL) {
        printf("no file\n", "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;
}

helper函数可以在load函数之外正常工作,但是当我尝试在加载后打印列表时,什么也没打印出来.我想念什么?

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?

我也刚发布了辅助函数

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:现在已修复,感谢所有发表评论的人,您可以阅读所有评论,也可以只是 https://stackoverflow.com /a/44078897/8038340

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

推荐答案

使用 printf() scanf() 令人惊讶难的.可以对它们进行各种魔术处理,但是您需要知道它们如何工作才能执行该魔术处理.

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.

在示例代码中,通过在输出中不包括记录定界符,使自己的生活更加困难.换行符是常规且最简单的定界符,但是您可以选择其他定界符,也可以不定界符.但是,如果不选择定界符,则必须知道问题中未提供的有关数据的信息.如果字符串从不包含空格,则可以减少格式的严格度.但是,您必须有某种方法知道一个数字在哪里结束,而下一个数字在哪里开始-您不能像示例printf()格式那样简单地将所有数字混在一起,除非它们都为负数,或者在其上加一个加号正数(%+d).必须有某种方法来告诉scanf()什么时候停止读一个数字并从下一个数字开始.

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.

这段代码是我在众多评论中所写内容的详尽说明.输出格式使用固定宽度的字段;这使阅读它们变得更容易.它不假定字符串中没有空格,因此它使用%29c读取29个字符,并添加一个空终止符并通过strip_blanks()删除尾随空格.它包括打印列表的代码;它使用该代码.

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\n", 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--] = '\0';
    while (offset > 0 && data[offset] == ' ')
        data[offset--] = '\0';
}

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'\n", 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] = '\0';
}

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\n", 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]\n", sensor->id, sensor->intervalo,
           sensor->active, sensor->local, sensor->tipo);
}

static void print_sensor_list(const char *tag, Sensor *list)
{
    printf("%s:\n", 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;
}

运行时,将产生输出:

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]

输出文件sensores.txt如下所示:

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

拆分为记录时,即:

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

11的整数宽度允许在前两列中的每一列中使用负32位数字.如果您知道数字较小,则可以减少使用的空间.在scanf()中,您可以省略整数字段的长度.因为数字格式会自动跳过空格,所以它的工作原理相同. printf()可以添加换行符;扫描代码根本不需要更改,因为scanf()期望数字(或字符串时,它并不关心换行符-仅%c%[…]扫描集和%n不会跳过前导白)空间).

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).

您还可以安排一些不会出现在字符串中的字符(也许是Control-A,'\1')来分隔字符串.然后,扫描代码可能会寻找该信息,并且您可能会得到可变长度的输出.

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.

在我自己的设备上,我可能会使用带有换行符的变长记录作为记录定界符,并为两个字符串使用合适的字段分隔符,以及不太严格的scanf()格式.我已经阅读了 fgets() 或POSIX的内容 getline() ,然后使用 sscanf() .除非您的字符串中可以包含换行符,否则这将很好地工作.

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:

有关完整详细信息,请阅读printf()scanf()的POSIX规范.它们确实在标准C printf()scanf()上有一些(明显标记)扩展,但它们同时适用于POSIX和标准C.然后重新读取它们.并重新阅读它们.并每天执行一个星期,然后每周执行一个月,然后每月执行一年,然后每年执行一次.它将付出努力.

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.

这篇关于从链接列表保存到文件并重新加载的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆