将链表保存并加载到二进制文件(C)

时间:2016-06-14 20:42:03

标签: c linked-list

我正在尝试从C中的二进制文件中编写和读取链接列表。 我的目标是为护理院保存和加载居民数据(实际上,我是护士),以便通过资源利用组对每个居民进行分类。我已经使用一系列结构为固定数量的居民(32,即设施的容量)做了这个,但现在我需要为一组可变的居民做这样的事情,以便进行统计研究。 显然,对于第一次尝试,我将结构简化为最小,因为实际结构包含109个数据。

我非常接近解决方案,但有些东西不起作用,也就是说,保存列表的每个元素都替换为void one.This代码应从二进制文件中读取列表,在终端上显示,添加一个新元素,保存列表。当然,每个程序都应该放在一个函数中。

GC

1 个答案:

答案 0 :(得分:2)

你是如此接近,我甚至不确定失败的真正原因是什么,因为,对于第一次切割,我只是应用了[大部分]其他人建议的修复并得到了一个有效的程序。 / p>

虽然它有效,但我发现你提前做了一个"提前一步"当你扩展程序做其他事情时,make_structure要求不那么灵活。

例如,你有一个挂鬼"记录如果您决定添加新记录,而是添加新记录,而是对现有记录进行一些统计或操作。

所以,我创建了该程序的第二个版本,它具有更多的隔离性和通用性。

这里有最低限度的版本[请原谅无偿的风格清理]:

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

struct pat {
    char surn[16];
    char name[16];
    struct pat *next;
};

static FILE *h;
static struct pat *osp;
static struct pat *first;

struct pat *make_structure(void);

int
main()
{
    int rlen;

    initscr();
    raw();
    keypad(stdscr, TRUE);
    noecho();

    clear();

    osp = make_structure();
    first = osp;

    h = fopen("archivio", "r");

    if (h == NULL)
        printw("Archivio inesistente\n");
    else {
        while (1) {
            printw("Lungh. nome = %d\n", sizeof osp->name);

            // leave early on EOF or badly formed entry
            rlen = fread(osp->surn, sizeof osp->surn, 1, h);
            if (rlen != 1)
                break;

            // leave early on EOF or badly formed entry
            fread(osp->name, sizeof osp->name, 1, h);
            if (rlen != 1)
                break;

            printw("Cognome: %s\tNome: %s\n", osp->surn, osp->name);

            osp->next = make_structure();
            osp = osp->next;
        }
        fclose(h);
    }

    // NOTE: this just chews the first character (debug, I suppose?)
#if 0
    getch();
#endif

    // add new element
    echo();
    printw("Surname: ");
    scanw("%15s", osp->surn);
    printw("Name: ");
    scanw("%15s", osp->name);

    noecho();

    h = fopen("archivio", "w");

    osp = first;
    while (osp != NULL) {
        fwrite(osp->surn, sizeof osp->surn, 1, h);
        fwrite(osp->name, sizeof osp->name, 1, h);
        osp = osp->next;
    }

    fclose(h);

    return 0;
}

struct pat *
make_structure(void)
{
    struct pat *a;

    a = malloc(sizeof(struct pat));

    // NOTE: do this for good measure
    a->next = NULL;

    return (a);
}

这是更广泛的版本,可以在扩展程序功能时为您提供一些想法:

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

struct pat {
    char surn[16];
    char name[16];
    struct pat *next;
};

static FILE *h;
static struct pat *osp;
static struct pat *first;
static struct pat *prev;

void read_archive(const char *file);
void add_new_elements(void);
void write_archive(const char *file);
struct pat *make_structure(void);
void add_to_list(struct pat *pat);

int
main()
{

    initscr();
    raw();
    keypad(stdscr, TRUE);
    noecho();

    clear();

    read_archive("archivio");

    // NOTE: this just chews the first character (debug, I suppose?)
#if 0
    getch();
#endif

    // NOTE: instead of just automatically adding new elements, this might
    // be replaced with a menu, such as:
    //   Enter Operation:
    //     (1) Add new names
    //     (2) Calculate statistics
    //     (3) Backup database
    add_new_elements();

    write_archive("archivio");

    return 0;
}

// read_archive -- read in archive
void
read_archive(const char *file)
{
    int rlen;

    h = fopen(file, "r");

    if (h == NULL)
        printw("Archivio inesistente\n");

    else {
        while (1) {
            osp = make_structure();

            // leave early on EOF or badly formed entry
            rlen = fread(osp->surn, sizeof osp->surn, 1, h);
            if (rlen != 1)
                break;

            // leave early on EOF or badly formed entry
            fread(osp->name, sizeof osp->name, 1, h);
            if (rlen != 1)
                break;

            printw("Cognome: %s\tNome: %s\n", osp->surn, osp->name);

            add_to_list(osp);
        }

        // NOTE: this is _always_ for EOF or bad entry, so free it
        free(osp);

        fclose(h);
    }
}

// add_new_elements -- prompt for new elements
void
add_new_elements(void)
{

    echo();
    while (1) {
        osp = make_structure();

        printw("Surname: ");
        osp->surn[0] = 0;
        scanw("%15s", osp->surn);
        if (osp->surn[0] == 0)
            break;

        printw("Name: ");
        osp->name[0] = 0;
        scanw("%15s", osp->name);
        if (osp->name[0] == 0)
            break;

        add_to_list(osp);
    }
    noecho();

    free(osp);
}

// write_archive -- write out archive
void
write_archive(const char *file)
{

    h = fopen(file, "w");

    for (osp = first;  osp != NULL;  osp = osp->next) {
        fwrite(osp->surn, sizeof osp->surn, 1, h);
        fwrite(osp->name, sizeof osp->name, 1, h);
    }

    fclose(h);
}

struct pat *
make_structure(void)
{
    struct pat *a;

    a = malloc(sizeof(struct pat));

    // NOTE: do this for good measure
    a->next = NULL;

    return (a);
}

void
add_to_list(struct pat *pat)
{

    if (first == NULL)
        first = pat;
    else
        prev->next = pat;

    prev = pat;
}

<强>更新

  

我还在试图找出失败的原因

我没有调试/单步执行您的原始代码,因为我认为您的链接列表逻辑需要修复,我想快速完成。然而,在我审查之后,逻辑很好。根据我的最佳猜测分析,可能的失败是feof我已经更改为fread的长度检查。

  

当然我打算用功能组织更好的程序

我以为你愿意。第二个程序中的拆分更像是一个教学工具,用于澄清和说明一个原则,并且不是对模块性的批评。

在原始代码中, 添加新记录,因为osp为空但已链接到列表中。松散地,一个&#34;僵尸&#34;记录,如果你愿意。

也就是说,该列表在之前链接了一个条目它已被填写并验证。换句话说,在读取循环之后,但在提示用户输入新条目之前,列表可能被视为格式错误(即[小]违反&#34;合同编程&#34;或者&#34;按合同设计&#34;原则)。

第二个程序中的功能拆分只是为了强调这一点。特别是,通过将读取循环移动到单独的函数,它说明/强制执行按合同设计。

也就是说,在进入时,列表是完整的并且格式良好[尽管是空的]。返回时,它为空[如果输入文件存在]或者其中只有格式良好/完整的记录。

在第二个程序中,部分/格式错误的条目永远不会链接。add_to_list总是最后完成[仅限整个/完整记录]。

因此,对于read_archiveadd_new_entries,当它们被调用时,它们都被给予一个完整/完整的列表,其中只包含有效的完整形式的记录。这是&#34;合同&#34; 他们

并履行他们的合同&#34;这些函数必须保持相同的方式,在退出时保持列表的完整性。这就是功能&#39; &#34;合同&#34;对外界

更新#2:

  

请原谅我的OT,但是你能否建议我使用一个适用于Debian / GNU Linux的C-C ++的好IDE?

我可能不是最好的人就此建议你,因为我没有使用它。我在他们存在之前很早就写了C,所以我开发了自己的工具套件,它比我见过的任何IDE都强大得多。而且,当我看着它们时,我找不到一种方法来对这两者进行网格化。

  

我在家里使用Code :: Blocks,但不幸的是,所谓的夜间构建是错误的并经常崩溃

如果您在家中使用代码块,但每晚构建有问题,也许简单的解决方案是将您的更新切换到&#34;稳定&#34;树,如果可能的话。这可能是最好的答案&#34;。

  

(代码完成实用程序非常有用,但我不能输入str ...,否则它会冻结),这非常令人沮丧!

也许您可以查看错误数据库,看看您遇到的问题是否已知错误报告。如果没有,你可以/应该提交一个。

我安装了codeblocks。它看起来干净简单。我还安装了eclipse并查看了kdevelop。从几个网页中,eclipse获得高分,netbeans接近第二

我尝试在源文件上使用它们,我躺在那里用Makefile构建。 codeblocks非常直观,我可以快速完成。我和其他人一起遇到了更多麻烦。 eclipse最初由IBM设计用于内部使用,然后作为公共服务发布。它得到了很好的支持和成熟。

我一直在运行没有 CDT的eclipse ,但是一旦我添加了它,eclipse得到了我的投票,因为它似乎有足够的功能来控制我所有的东西在下面抱怨; - )

IDE是一种个人选择[除非贵公司要求],所以你应该使用你喜欢的东西。换句话说,尝试一下,看看他们有什么功能以及它们的工作原理。这是一个列出一些页面的页面:https://en.wikipedia.org/wiki/Comparison_of_integrated_development_environments

选择IDE时,您必须查看&#34;最常用的&#34;特征。您最常做的事情是在源文件中滚动。因此,编辑器应支持箭头键的hjkl别名,如vi。不得不将你的右手移到箭头键和后面,这会使事情变得非常缓慢,以至于它不会起作用。

eclipse使用gvim [图形vim],所以这是一个加号。

是一个使用简单的所见即所得编辑器窗格进行编辑的粉丝,该窗格只有粗略的搜索/替换功能。同样地,vim只需键入/即可进行正则表达式搜索,因此,最常见的操作是&#34;触手可及&#34;

我没有使用[或想要]自动完成功能。当我尝试过它们时,它们经常会出错,并且需要更长时间才能完成它们所做的事情。我打字速度非常快。

我还关闭了源的语法高亮和着色。在输入源代码时,由于编辑器认为我正在打字,所以输入的每个字符的颜色都会发生变化(例如我输入了一条评论,但它认为它是&#39;我的代码,等等。我发现这会分散注意力。

另外,在查看最终结果时,我发现彩色结果太忙了#34; (即我需要过滤的更多信息),而不是帮助我看到我需要看到的东西。

我非常坚持缩进,用空行分解长代码块以提高可读性。当然还有好的评论。对我来说,这些比着色要重要得多。我有一个缩进的自定义工具[你可能还记得,当我发布上面的代码时,它被重新缩进,因为我在发布之前通过我的工具运行它。

另一个功能是图形调试器。它是全功能的吗?例如,ddd是围绕[非常强大的] gdb的图形包装器。 ddd为常见事物提供了图形包装器和窗口,但仍允许gdb提示符的直接文本窗口,因此您可以手动键入更高级的命令(例如watch symbol)。

IDE是否可扩展?你能加点插件吗?你能轻松添加/创建自己的吗?

IDE使用什么源代码控制系统?多年来我一直使用很多,现在,我已在git完全销售。因此,如果IDE不支持git,那么它就不是首发。

应该注意的是,git具有所以许多功能,而不是包含在GUI中。因此,真正强大的东西在终端窗口中使用命令行工具。

我的IDE?几个xterm窗口,vigit和我的工具套件[目前有250,000行perl脚本;-)] IDE会强迫您按照自己的方式行事吗?将配置等导入/导出到其他外部工具和IDE有多容易?

我有一个非常强大的自己设计的构建脚本。所以,当我点击&#34; build&#34;时,我想要IDE。按钮执行通常的操作,但将控制权交给我的构建脚本。同样,对于IDE必须提供的任何其他操作。

IDE是否可移植并可在所有主要平台上使用:Linux,OSX和Windows?在过去,这是我回避IDE的另一个原因。它们只能在一个平台上使用。或者,由于我正在进行咨询工作,因此我会进入一个不允许通过[sysadmin]策略安装/使用IDE的环境。