在下面的示例程序中,我将演示C标准库提供的格式化文件读取函数的基本用法。
示例程序是一个拼写检查实用程序,它可以扫描文本文件中的单词,并将单词与字典文件中的单词列表进行比较。我使用的字典文件是从GitHub下载https://github.com/dwyl/english-words。(我使用了该存储库中包含的文件word_alpha.txt。该文件中的原始单词列表没有正确地按字母顺序排列,因此我编写了一个小实用程序来正确地按字母顺序排列单词列表。)
下面是我在程序中使用的#include语句列表。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> .h
h头文件包括处理标准输入和输出的函数,包括格式化的文件I/O。我们将在示例程序中使用许多这样的文件I/O函数。您可以在教科书第三章“标准I/O”一节中阅读更多关于这些文件I/O函数的信息。
由于我们将使用malloc()和free()函数进行内存分配,因此我们包含了stdlib.h。
为了使用strcmp()函数进行字符串比较,并通过strtok()函数从一行文本中提取单个单词,我们包含了string.h。
为了通过lower()函数将字母从大写转换为小写,我们包含了ctype.h。
我们将把单词存储在C结构体dictionary的拼写检查字典中。下面是该结构体的定义:
typedef struct {int N;// char** words中的单词数;//单词数组};
字典中的单词将存储在一个数组words中,该数组包含指向单个单词的指针。结构声明只声明指向指针数组的指针。在初始化步骤中设置这个数组并用单词填充它。
字典中的单词存储在名为words.txt的文本文件中。loadDictionary()函数负责读取单词并在字典结构中设置单词数组。
dictionary* loadDictionary(const char* fileName) {FILE* f = fopen(fileName,"r");if(f == NULL)返回NULL;//计算字典中的单词个数;int lineCount = 0;While (! fof (f)) {ch = fgetc(f);if(ch == '\n') lineCount++;} lineCount + +;//单词数比换行数多1。倒带(f);Dictionary * d = (Dictionary *) malloc(sizeof(Dictionary));d->N = lineCount;if(lineCount == 0) {d->words = NULL;} else {d->words = (char**) malloc(lineCount * sizeof(char*));字符缓冲区[64];Int n = 0;While (! fof (f)) {fscanf(f,"%s",buffer);Char * word = (Char *) malloc(strlen(buffer)+1);拷贝字符串(单词,缓冲);D ->words[n] = word;n + +;}} fclose(f);返回d;}
这个函数将读取word .txt文件两次。在第一次传递时,它将通过计算文件中的换行字符数来简单地计算文件中的单词数。在第二次传递时,它将读取单词并将其存储在字典结构中。
要打开word .txt文件,我们使用带有模式字符串“r”的fopen()函数来表示我们希望以读取模式打开文件:
FILE* f = fopen(fileName,"r");
关闭文件使用fclose():
文件关闭(f);
在第一次遍历文件之后,我们使用rewind()函数将文件倒回到开始处。
要计算文件中的单词数,我们使用以下逻辑:
//计算字典中的单词个数;int lineCount = 0;While (! fof (f)) {ch = fgetc(f);if(ch == '\n') lineCount++;} lineCount + +;
这段代码使用fgetc()函数从输入文件中每次读取一个字符。这里我们使用feof()函数来控制循环,并在到达文件末尾时停止读取。
一旦我们确定了字典文件中有多少单词,我们就可以设置字典结构。这需要调用malloc()来为结构体、指向单词的指针数组和单个单词分配内存。下面是设置字典的代码:
Dictionary * d = (Dictionary *) malloc(sizeof(Dictionary));d->N = lineCount;if(lineCount == 0) {d->words = NULL;} else {d->words = (char**) malloc(lineCount * sizeof(char*));字符缓冲区[64];Int n = 0;While (! fof (f)) {fscanf(f,"%s",buffer);Char * word = (Char *) malloc(strlen(buffer)+1);拷贝字符串(单词,缓冲);D ->words[n] = word;n + +;}}
malloc()函数接受一个参数,即要分配的内存字节数。要确定结构体和数组需要多少字节,可以使用sizeof()函数来确定字典结构体和指向char数组的指针所需的字节数。
为了从字典文件中读取单个单词,我们使用fscanf()函数和格式说明符%s来表示我们希望从文件中读取单个单词。fscanf()调用中的最后一个参数是指向我们希望存储单词的字符数组的指针。对于从文件中读取的每个单词,我们分配一个字符数组,其大小刚好足以容纳单词中的字母加上结束字符‘\0’。然后通过strcpy()函数将单词从临时缓冲区复制到单词数组中。一旦我们为一个特定的单词建立了word数组,我们就在字典的words数组中存储一个指向该单词的指针。
在设置字典结构时,我们在不同的地方使用malloc()为结构体、单词数组和单个单词分配内存。当我们使用完这些项时,为它们释放内存是一个很好的编程实践。freeDictionary()函数负责这个:
void freeDictionary(dictionary* d) {for(int n = 0;n < d- b> n;n++) free(d- b> words[n]);免费(d - >字);自由(d);}
要使用字典进行拼写检查,我们需要编写一个函数,该函数可以搜索字典中的单词列表,以确定给定单词是否在字典中。searchDictionary()函数使用一个高效的二进制搜索来完成这个搜索:
int searchDictionary(dictionary* d,const char* word) {int start = 0;int end = d->N-1;While (start <= end) {int mid = (start+end)/2;Const char* lookup = d->words[mid];Int comp = strcmp(word,d->words[mid]);If (comp == 0) return mid;if(comp < 0) end = mid -1;Else start = mid + 1;}返回-1;}
在每一轮搜索中,代码使用strcmp()函数将我们要搜索的单词与字典列表中的单词进行比较。如果单词与字典中的单词完全相同,则此函数将返回0。如果单词按字母顺序出现在字典单词之前,strcmp()将返回一个负整数。如果单词在后面,strcmp()返回一个正整数。
最后,我提供了一个函数,该函数打开第二个文本文件并检查该文件中的每个单词,以查看它是否出现在字典中。
void checkWords(dictionary* d,const char* fileName) {FILE* f = fopen(fileName,"r");if(f == NULL) {printf(“无法打开数据文件。\n”);返回;} char line[256];char* word = NULL;const char * delims = " 0123456789 !@#$&?%*+-/<>,.;:(){}[]\"\'\ n \ r \ t”;While (! fof (f)) {fgets(line,255,f);Word = strtok(line,delims);while(word != NULL) {int n = 0;而(字[n] ! = ' \ 0 '){词[n] =低(词[n]);n + +;} int loc = searchDictionary(d,word);If (loc == -1) printf("%s\n",word);word = strtok(NULL,delims);}} fclose(f);}
这个函数使用一种稍微不同的方法来读取文本文件中的单个单词。我们通过调用fgets()函数一次一行地读取文本文件。为了将这一行分割成单独的单词,我们使用strtok()函数。strtok()的第二个参数是一个分隔符数组。strtok()函数在扫描该行寻找单词时应该跳过这些字符。如您所见,我们在这里列出了几乎赢博体育可能的非字母字符作为分隔符。
strtok()函数有一个有点特殊的使用模式。第一次对一行文本调用strtok()时,将指向文本数组的指针作为第一个参数传递给strtok()。在随后的调用中,我们将NULL传递给strtok()。这告诉函数,我们希望在第一次调用时传递的相同数组中继续向前扫描。
每次strtok()在数组中找到一个单词时,它都会传递给我们一个指向该行中该单词的指针。当strtok()在该行中找不到任何其他单词时,它将返回NULL。
对于从文本文件中读取的每个新单词,我们调用searchDictionary()来确定该单词是否在字典中。如果不是,searchDictionary()将返回一个-1的位置。我们将字典中没有的赢博体育单词打印到控制台。