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



我有一个平面文件(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: 

让我们从一个非常基本的方法开始,该方法将平面文件用于存储,在每个查询中,在平面文件上循环,试图将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,从密钥的末尾修剪'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字符与key进行比较,如果匹配,则打印并设置found标志,然后中断文件读取循环。最后,如果没有设置found,让用户知道没有找到密钥,现在再做一次,直到用户在请求主密钥的行上单独按下输入

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 = %snAddress = %sn"
"Salary = %un", 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 = %snAddress = %sn"
"Salary = %un", 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:

仔细看看,如果你还有问题,请告诉我。

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

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

一般而言;你想要:

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

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

b2)一种确定"线路末端"是什么的方法(例如,如果是"n""rn""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"被接受,或者有其他规则,其中每个规则都需要自己的漂亮的描述性错误消息)。请注意,最后一个函数(用于处理数字)可能会返回某种类型的整数

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

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

d2)"添加这个结构"函数,它将一个新的结构添加到哈希表中(或者你决定用来组织结构的任何东西)。

e) 一个"查找这个主键"函数,它使用您已经拥有的代码来检查和解析用户的输入,然后使用您已经具备的代码来计算"检查和解析的健全性"主键的哈希;然后使用散列(以及"健全性检查和解析"主键)来找到正确的结构。

最新更新