1

我有一个平面文件(txt 文件),其中包含一些字段:

主键、姓名、地址、薪水 - 由 | 分隔 喜欢:

A0001|John|New York City|12000
A0002|Daisy|New Delhi|32000
A0003|Dany|London|23000

.. ETC

当我有主键时,如何开发代码来搜索平面文件中的每个数据?并且它的每个数据都存储到特定的变量中以供下一个目的。

我想要的结果是:

Input the primary key: A0002

Result:
Name = Daisy
Address = New Delhi
Salary = 32000

Input the primary key: 
4

2 回答 2

1

让我们从一种非常基本的方法开始,该方法使用平面文件进行存储,并且在每个查询中,循环遍历平面文件以尝试将您key与第一个字段匹配。如果找到匹配项,只需使用分隔值(使用赋值抑制修饰符sscanf忽略第一个字段到该字段的转换说明符,例如)。打印值。如果在文件中找不到密钥,也请这样说。sscanf'*'

使用fgets()for input,您可以简单地进行输入,直到用户按下Enter该键的空白行。(您只需检查第一个字符是否是'\n',如果是,则中断循环)。

您不断循环以允许进行多个键查询,rewind()每次循环开始时都使用回退到文件的开头。

将它们放在一起,您可以首先打开并验证您的文件是否已打开以供阅读。

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

#define MAXN   64   /* if you need a constand, #define one (or more) */
#define MAXC 1024   /* (don't skimp on buffer size) */

int main (int argc, char **argv) {

    /* use filename provided as 1st argument ("flatfile.txt" by default) */
    FILE *fp = fopen (argc > 1 ? argv[1] : "flatfile.txt", "r");

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

现在开始你的持续循环,它将key从用户那里读取,从键的末尾修剪'\n'(你不希望它作为比较的一部分),并获取键的长度:

    for (;;) {  /* loop continually until [Enter] on empty line */
        char buf[MAXC], name[MAXN], city[MAXN], key[MAXN];
        unsigned salary, len, found = 0;

        rewind (fp);    /* rewind file to beginning */
        fputs ("\nInput the primary key: ", stdout);    /* prompt for key */
        if (!fgets (key, MAXN, stdin) || *key == '\n')  /* read key */
            break;
        key[strcspn (key, "\n")] = 0;                   /* trim '\n' */
        len = strlen(key);                              /* get key length */

有了这些信息,现在循环遍历文件,将每一行读入缓冲区buf,然后strcmp将第一个len字符与您keyfound. 最后如果found没有设置,让用户知道没有找到密钥,现在再做一次,直到用户Enter单独按下要求主键的行:

        while (fgets (buf, MAXC, fp)) {                 /* read each line */
            if (strncmp (buf, key, len) == 0) {         /* compare key */
                /* parse line into separate values, ignoring 1st key field */
                if (sscanf (buf, "%*63[^|]|%63[^|]|%63[^|]|%u",
                                name, city, &salary) == 3) {
                    printf ("\nResult:\nName = %s\nAddress = %s\n"
                            "Salary = %u\n", name, city, salary);
                    found = 1;  /* set flag indicating key found */
                    break;      /* no sense in reading rest */
                }
            }
        }
        if (!found) /* if key not found, so indicate */
            fputs ("\nResult: (not found)\n", stdout);
    }

剩下的就是关闭输入文件。一个完整的例子是:

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

#define MAXN   64   /* if you need a constand, #define one (or more) */
#define MAXC 1024   /* (don't skimp on buffer size) */

int main (int argc, char **argv) {

    /* use filename provided as 1st argument ("flatfile.txt" by default) */
    FILE *fp = fopen (argc > 1 ? argv[1] : "flatfile.txt", "r");

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    for (;;) {  /* loop continually until [Enter] on empty line */
        char buf[MAXC], name[MAXN], city[MAXN], key[MAXN];
        unsigned salary, len, found = 0;

        rewind (fp);    /* rewind file to beginning */
        fputs ("\nInput the primary key: ", stdout);    /* prompt for key */
        if (!fgets (key, MAXN, stdin) || *key == '\n')  /* read key */
            break;
        key[strcspn (key, "\n")] = 0;                   /* trim '\n' */
        len = strlen(key);                              /* get key length */

        while (fgets (buf, MAXC, fp)) {                 /* read each line */
            if (strncmp (buf, key, len) == 0) {         /* compare key */
                /* parse line into separate values, ignoring 1st key field */
                if (sscanf (buf, "%*63[^|]|%63[^|]|%63[^|]|%u",
                                name, city, &salary) == 3) {
                    printf ("\nResult:\nName = %s\nAddress = %s\n"
                            "Salary = %u\n", name, city, salary);
                    found = 1;  /* set flag indicating key found */
                    break;      /* no sense in reading rest */
                }
            }
        }
        if (!found) /* if key not found, so indicate */
            fputs ("\nResult: (not found)\n", stdout);
    }
    fclose (fp);   /* close file */
}

示例输入文件

$ cat dat/flatfile.txt
A0001|John|New York City|12000
A0002|Daisy|New Delhi|32000
A0003|Dany|London|23000

示例使用/输出

$ ./bin/readflatfile dat/flatfile.txt

Input the primary key: A0002

Result:
Name = Daisy
Address = New Delhi
Salary = 32000

Input the primary key: A0003

Result:
Name = Dany
Address = London
Salary = 23000

Input the primary key: A0004

Result: (not found)

Input the primary key:

如果您还有其他问题,请仔细查看并告诉我。

于 2019-10-21T06:42:28.223 回答
0

因为您将多次搜索数据,所以您希望以更有效(用于搜索)的方式将数据存储在内存中。

此外,您需要发现数据的任何问题并以可接受的方式处理它们(生成错误/警告消息,并跳过错误行和/或如果文件格式错误则拒绝使用文件)。

一般来说; 你要:

a) 一种将原始数据(例如字节)放入内存的方法。这可能会被破坏(例如fgets(),它被破坏且无法使用,因为它取决于可能是错误的“行尾”的默认编码);或者可以是一个循环,read()将整个文件加载到缓冲区中,或者(理想情况下)可以是mmap().

b1) 一种确定预期字符集和编码的方法(例如 EBDIC、ASCII、UTF-8、UTF-16LE、...)。这可能就像说(例如)“文件必须是 UTF-8”一样简单,也可能像支持许多不同的字符集和编码一样复杂,并使用某种方案自动检测使用了哪个文件(例如<meta charset="UTF-8">HTML 中的文件)。

b2) 一种确定“行尾”是什么的方法(例如,如果它是"\n"or"\r\n""\r",它们都是不同操作系统的“标准”)。为此,可以以可靠的方式自动处理它;通过将'\n''\r'视为“行尾”,然后忽略下一个字符,如果它是相反的(例如,如果你看到'\n'那么它是一行的结尾,如果下一个字符是'\r'它将被忽略,如果你看到'\r'那么它是一行的结尾,如果下一个字符是'\n'它将被忽略)。另请注意,“文件结尾”可以(应该)被视为“文件中最后一行的结尾”,可能带有“在文件末尾找不到行尾”警告。

b3)一个循环,将原始字节转换为“程序标准”编码和字符集,一次一个字符,同时检测和处理对文件使用的任何编码和字符集无效的内容,直到“结束行”(或“文件结尾”;然后调用“处理这一行”函数,然后递增一个“line_number变量(将用于错误消息 - 例如,您可以显示错误消息,如“无效序列在第 33 行找到 UTF-8 的字节数”,并使人们更容易在文本文件中找到/修复问题。

c) 一个“处理这条线”功能,它:

  • 将原始字符串拆分为 4 个子字符串(在'|'字符处),并丢弃 4 个子字符串中每一个的前导和尾随空格,并且可以将多个空格字符转换为单个空格字符(例如,"A0001 | John |\t New York City |12000"变成"A0001", 和) . 这也应该处理错误 - 例如,如果没有足够的字符使该行有效。这个函数也可以默默地忽略空行和只包含空格的行;并且还可以默默地忽略特殊字符之后的所有内容(例如,您可以支持评论,例如可能)。"John""New York City""12000"'|'"A0001|John|New York City|12000 # This dude owes me money!"

  • 一组函数,用于根据实际要求解析和验证每个字符串。例如,可能主键必须以大写字母开头(因此您需要一个生成“主键无效(不以大写字母开头)”错误的函数),并且“名称”部分必须小于 128字符,并且末尾的数字 ( "12000") 必须在特定范围内(并且可以/不能包含逗号,因此"12,000"可以接受,或者有其他规则,其中每个规则都需要自己的良好描述性错误消息)。请注意,最后一个函数(用于处理数字)可能会返回某种整数。

  • 为结构(字面意思是 a struct my_thing { ....})分配内存的代码,并将“完整性检查和解析”数据放入结构中;然后调用“添加此结构”函数。

d1)某种组织结构对搜索有效的方法(这是您最常见/最重要/唯一的用例)。为此,我可能会使用哈希表(使用主键生成哈希以用作链接列表表的索引)。

d2)“添加此结构”功能,将新结构添加到哈希表(或您决定用来组织结构的任何结构)。

e)“查找此主键”功能,它使用您已经拥有的代码来进行完整性检查和解析用户的输入,然后使用您已经拥有的代码来计算“完整性检查和解析”主键的哈希值;然后使用散列(和“完整性检查和解析”的主键)找到正确的结构。

于 2019-10-21T06:09:44.820 回答