如何读取平面文件并将数据放入相应的变量

How to read a flat file and put the data to corresponding variable

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

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

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

.. 等等

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

我想要的结果是:

Input the primary key: A0002

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

Input the primary key: 

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

此外,您需要发现数据的任何问题并以接受table的方式处理它们(生成error/warning消息,或者跳过错误的行and/or拒绝如果文件格式不正确,请使用该文件)。

一般;你想要:

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

b1) 一种确定预期字符集和编码(例如 EBDIC、ASCII、UTF-8、UTF-16LE 等)的方法。这可以像说(例如)"the file must be UTF-8" 一样简单,也可以像支持许多不同的字符集和编码一样复杂,并具有一些自动检测文件使用的方案(例如 [=99 中的 <meta charset="UTF-8"> =]).

b2) 一种确定 "end of line" 是什么的方法(例如,如果它是 "\n""\r\n""\r",它们都是 "standard" 用于不同的操作系统)。为此,可以以可靠的方式自动处理它;通过将 '\n''\r' 视为 "end of line" 但如果下一个字符相反则忽略下一个字符(例如,如果您看到 '\n' 那么它是一行的结尾,如果下一个字符是 '\r' 它将被忽略,如果你看到 '\r' 那么它是一行的结尾,如果下一个字符是 '\n' 它将被忽略)。另请注意,"end of file" 可以(应该)被视为 "end of last line in the file",可能带有 "no end of line found at end of file" 警告。

b3) 将原始字节转换为 "standard for your program" 编码和字符集的循环,一次一个字符,同时检测和处理对于文件使用的任何编码和字符集无效的内容, 直到达到 "end of line" (或 "end of file" ;然后调用 "process this line" 函数,然后递增 "line_number 变量(它将用于错误消息 - 例如,这样你就可以显示类似 "Invalid sequence of bytes for UTF-8 found on line 33" 的错误消息,并使人们更容易 find/fix 文本文件中的问题。

c) 一个 "process this line" 函数,其中:

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

  • 一组函数,根据实际需要解析和验证每个字符串。例如,也许主键必须以大写字母开头(所以你需要一个产生 "Primary key is invalid (doesn't start with capital letter)" 错误的函数),并且 "name" 部分必须小于 128 个字符,最后的数字("12000") 必须在特定范围内(并且 can/can 不包含逗号,因此 "12,000" 被接受,或者有其他规则,其中每个规则都需要自己漂亮的描述性错误消息).请注意,最后一个函数(用于处理数字)可能 return 某种整数。

  • 代码为结构体(字面意思是struct my_thing { ....})分配内存,并将"sanity checked and parsed"数据放入结构体;然后调用一个 "add this structure" 函数。

d1) 某种有效搜索结构的方式(这是您最 common/most important/only 用例)。为此,我可能会使用散列 table(使用主键生成散列以用作 table 链表的索引)。

d2) "add this structure" 函数将新结构添加到散列 table(或您决定用于组织结构的任何内容)。

e) 一个 "find this primary key" 函数,它使用您已经拥有的代码来完整性检查和解析用户的输入,然后使用您已经拥有的代码计算 "sanity checked and parsed" 主节点的哈希值钥匙;然后使用散列(和 "sanity checked and parsed" 主键)找到正确的结构。

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

使用 fgets() 进行输入,您可以简单地进行输入,直到用户在该键的空白行上按下 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,从密钥末尾读取 trim '\n'(你不希望它作为比较),得到key的长度:

    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 个字符与您的 key 进行比较,如果有匹配项,打印它并设置 found 标志并中断文件读取循环。最后,如果未设置 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

示例Use/Output

$ ./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:

检查一下,如果您还有其他问题,请告诉我。