从链表保存到文件并加载回来

我无法从文件加载到链接列表,一整天都在尝试

首先,这是我的结构

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

这是我的保存function,我认为它的工作正常,因为文件被创建,内容就在那里。

 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); } 

现在我似乎无法使这项工作无论我读到什么是负载function。

inheritance人我有什么

 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; } 

辅助函数在加载函数之外工作正常,但是当我尝试在加载后打印列表时,没有任何内容被打印。 我错过了什么?

编辑:生病也只是发布帮助函数

 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

正确使用printf()scanf()非常困难。 可以用它们做各种魔法,但你需要知道它们是如何工作才能发挥魔力的。

在示例代码中,通过在输出中不包括记录分隔符,您将使自己的生活变得更加困难。 换行符是常规和最简单的分隔符,但您可以根据需要选择其他分隔符,也可以不选择分隔符。 但是,如果您选择无分隔符,则必须知道有关问题中未提供的数据的信息。 如果字符串永远不会包含空格,则格式化方面可能不那么严格。 但你必须有一些方法知道一个数字结束的位置和下一个数字的开始 – 你不能简单地将所有数字一起刷新,因为样本printf()格式除非它们都是负数,或者你添加一个加号正数( %+d )。 必须有一些方法告诉scanf()何时停止读取一个并开始下一个数字。

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

 #include  #include  #include  #include  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()可以添加换行符; 扫描代码根本不需要改变,因为当期望一个数字(或一个字符串 – 只有%c%[…]扫描集)时, scanf()不关心换行符,并且%n不会跳过前导空格)。

您还可以安排一些不会出现在字符串中的字符(可能是Control-A, '\1' )来分隔字符串。 然后扫描代码可以查找,你可以有可变长度输出。

留给我自己的设备,我可能会使用一个可变长度记录,其中记录分隔符使用换行符,两个字符串使用合适的字段分隔符,而不太严格的scanf()格式。 我用fgets()或POSIX getline()读取行,然后使用sscanf()扫描行。 除非你的字符串中有换行符,否则这样可以很好地工作。

正如我最近在另一个答案中所说的那样 – 轻描淡写:

阅读printf()scanf()的POSIX规范以获取完整的详细信息。 它们在标准C printf()scanf()有一些(明显标记的)扩展,但它们同时适用于POSIX和标准C.然后重新读取它们。 并重新阅读它们。 每天做一个星期,然后每周做一个月,然后每月做一年,然后每年做一次。 它将回报努力。

  1. fprintf(ficheiro, "%d%d%d%30s%30s" ……我建议你放一个分隔符,说昏迷或#。想象一下,你的id是11,intervalo是10,当保存时,它是1110.怎么做你知道,当从文件中读取时,id是11而不是1或111吗?

  2. 改变insereSensorFim(&l,insereSensorFim(l,

  3. 在insereSensorFim中,你使用while循环来找到尾部,它效率不高。 让* Lista始终指向尾部并跳过循环。 例如,

 void insereSensorFim(Sensor** tail, Sensor* novo) { if (*tail != NULL) { (*tail)->proximo = novo; novo->anterior = (*tail); } *tail = nova; } 

在写入文件时,您必须将整数分开,否则它们看起来就像读取函数的一个大数字。

您可以尝试替换fprintf(ficheiro, "%d%d%d%30s%30s", ...);fprintf(ficheiro, "%d;%d;%d;%29s;%29s", ...); (29而不是30,因为你没有写字符串终止'\0' )然后应该能够用fscanf(ficheiro, "%d;%d;%d;%29s;%29s", ...);读回fscanf(ficheiro, "%d;%d;%d;%29s;%29s", ...);

编辑:

在编写了一个较小的测试代码和一些调试之后,我发现如果你想在fscanf()的格式化中使用%s ,那么空格就会被删除字符串的末尾,它们会为你终止那么这会奏效:

 #include  #include  #include  void test_save(void) { FILE *ficheiro; ficheiro = fopen("sensores.txt", "r+t"); if (ficheiro == NULL) { ficheiro = fopen("sensores.txt", "w+t"); } fprintf(ficheiro, "%d;%d;%d;%-29s ;%-29s\n", 12, 138, 131, "Local_test", "Tipo_test"); fprintf(ficheiro, "%d;%d;%d;%-29s ;%-29s\n", 21, 218, 213, "Local_test_2", "Second_tipo_test"); fclose(ficheiro); } void test_read(void) { FILE *ficheiro; ficheiro = fopen("sensores.txt", "r+t"); if (ficheiro == NULL) { printf("no file %s\n", "sensores.txt"); return; } int id, intervalo, active; char local[30], tipo[30]; while (fscanf(ficheiro, "%d;%d;%d;%29s ;%29s\n", &id, &intervalo, &active, local, tipo) == 5) { printf("id: %d intervalo: %d active: %d\tlocal: [%s]\ttipo: [%s]\n", id, intervalo, active, local, tipo); } fclose(ficheiro); } int main(void) { test_save(); test_read(); } 

该测试程序的输出:

 id: 12 intervalo: 138 active: 131 local: [Local_test] tipo: [Tipo_test] id: 21 intervalo: 218 active: 213 local: [Local_test_2] tipo: [Second_tipo_test] 

正如本测试程序所写的文件所示,每条记录都写成一行:

 12;138;131;Local_test ;Tipo_test 21;218;213;Local_test_2 ;Second_tipo_test