1. 简介
在计算机编程领域中,统计单词个数是一个很基础的需求,常见于文本处理、搜索引擎等领域。本文将介绍如何使用C语言统计单词个数的方法,具体包括如何定义单词、如何读取文本、如何统计单词等。
2. 定义单词
在统计单词之前,需要先定义单词的概念。一般来说,单词是由字母组成的,包括大小写字母、数字、下划线等。但是,还需要考虑一些特殊情况:
单词中可能包含连字符“-”,例如“check-in”
单词中可能包含撇号“'”,例如“don't”
单词中可能存在缩写,例如“Mr.”、“U.S.”
基于以上考虑,我们可以将单词定义为满足以下条件的字符串:
由大小写字母、数字、下划线、连字符、撇号组成
长度不为0
连字符或撇号不能在单词的开头或结尾
缩写中只能包含大小写字母和点号,点号不能在开头或结尾
3. 读取文本
在进行单词统计之前,需要先读取待统计的文本。对于较小的文本,可以一次性读入内存,然后逐个单词进行统计;对于比较大的文本,需要分块读取,以避免内存不足的问题。
下面是一个读取文本的例子,可以读取整个文件并按单词进行划分:
#include <stdio.h>
#include <ctype.h>
#define MAX_WORD_LEN 100
int getword(char *word, int max_len)
{
int c, len = 0;
while ((c = getchar()) != EOF && !isalnum(c) && c != '-' && c != '\'');
if (c == EOF) return EOF;
do {
if (len < max_len - 1) word[len++] = tolower(c);
else break;
} while ((c = getchar()) != EOF && (isalnum(c) || c == '-' || c == '\'' || c == '.'));
word[len] = '\0';
return len;
}
void process_file(void)
{
char word[MAX_WORD_LEN];
int count = 0;
while (getword(word, MAX_WORD_LEN) != EOF) {
printf("%s\n", word);
count++;
}
printf("Total words: %d\n", count);
}
int main(void)
{
process_file();
return 0;
}
上述代码中定义了一个函数getword,它会读取一个单词并将其转换为小写字母,然后返回单词的长度。如果读取到文件末尾,函数返回EOF。在process_file函数中,逐个读取单词并打印出来,最后输出总单词数。
3.1 getword函数详解
getword函数中使用了一些C语言的库函数,这里进行详细解释:
getchar()
:从标准输入流中读取一个字符,返回其ASCII码值。
isalnum(c)
:判断字符c
是否为字母或数字,如果是返回非零值,否则返回0。
tolower(c)
:将字符c
转换为小写字母并返回其ASCII码值。
在getword函数中,首先调用getchar()
读取一个字符,如果遇到非字母数字或连字符或撇号,就继续读取下一个字符,直到遇到一个字母数字或连字符或撇号为止。然后,从该字符开始,逐一读取后面的字符,直到遇到非字母数字或连字符或撇号或点号为止,同时将读取到的字符转换为小写字母,并存储到word数组中。当读取的字符数量达到max_len-1
时,函数结束。
3.2 分块读取
当待读取的文件较大时,为避免内存不足的问题,可以采用分块读取的方式。具体地,每次读取固定长度的文本块,然后对每个文本块进行单词统计,最后将各个文本块的单词数累加起来。
下面是一个示例代码,每次读取文本块的长度为BLOCK_SIZE
:
#define BLOCK_SIZE 1024
void process_file(void)
{
char word[MAX_WORD_LEN];
int count = 0;
char buf[BLOCK_SIZE];
size_t n;
FILE *fp = fopen("file.txt", "r");
if (fp == NULL) {
perror("Error opening file");
return;
}
while ((n = fread(buf, 1, BLOCK_SIZE, fp)) > 0) {
const char *p = buf;
const char *end = buf + n;
while (p < end) {
const char *q = p;
while (q < end && isalnum(*q)) q++;
if (q > p) {
strncpy(word, p, q - p);
word[q - p] = '\0';
printf("%s\n", word);
count++;
}
p = q + 1;
}
}
fclose(fp);
printf("Total words: %d\n", count);
}
在该示例代码中,首先打开文件并检查是否成功打开,然后分块读取文件,每次读取的文本块大小为BLOCK_SIZE。在每个文本块中,逐个读取单词并进行统计,最终输出总单词数。
4. 统计单词
读取文件并将单词进行划分后,可以统计单词的数量了。具体地,可以使用哈希表等数据结构来存储单词及其出现次数。每次读取一个单词后,在哈希表中查找该单词,如果已存在,则将其出现次数加1;否则,将该单词加入哈希表并设置其出现次数为1。
下面是一个基于哈希表实现的单词统计代码:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#define MAX_WORD_LEN 100
#define HASH_TABLE_SIZE 10000
typedef struct hash_node {
char *key;
int value;
struct hash_node *next;
} hash_node_t;
hash_node_t **hash_table = NULL;
unsigned int hash(const char *key)
{
unsigned int hash_val = 0;
while (*key) {
hash_val = (hash_val << 4) + *key;
key++;
}
return hash_val;
}
void init_hash_table(void)
{
int i;
hash_table = (hash_node_t **)malloc(sizeof(hash_node_t*) * HASH_TABLE_SIZE);
for (i = 0; i < HASH_TABLE_SIZE; i++) {
hash_table[i] = NULL;
}
}
hash_node_t* lookup(const char *key)
{
hash_node_t *node;
unsigned int hash_val = hash(key) % HASH_TABLE_SIZE;
for (node = hash_table[hash_val]; node != NULL; node = node->next) {
if (strcmp(key, node->key) == 0) {
return node;
}
}
return NULL;
}
void insert(const char *key, int value)
{
hash_node_t *node;
unsigned int hash_val = hash(key) % HASH_TABLE_SIZE;
node = (hash_node_t*)malloc(sizeof(hash_node_t));
node->key = (char*)malloc(strlen(key) + 1);
strcpy(node->key, key);
node->value = value;
node->next = hash_table[hash_val];
hash_table[hash_val] = node;
}
void process_file(void)
{
char word[MAX_WORD_LEN];
int count = 0;
while (getword(word, MAX_WORD_LEN) != EOF) {
if (!lookup(word)) {
insert(word, 1);
} else {
lookup(word)->value++;
}
count++;
}
printf("Total words: %d\n", count);
}
void print_hash_table(void)
{
int i;
for (i = 0; i < HASH_TABLE_SIZE; i++) {
hash_node_t *node;
for (node = hash_table[i]; node != NULL; node = node->next) {
printf("%s : %d\n", node->key, node->value);
}
}
}
int main(void)
{
init_hash_table();
process_file();
print_hash_table();
return 0;
}
在上述代码中,定义了一个哈希表结构体hash_node_t
,其包含单词、出现次数和下一个节点的指针。定义一个全局变量hash_table
来存储哈希表。使用init_hash_table
函数来初始化哈希表,将所有指针置空。使用hash
函数将单词转换为哈希值。使用lookup
函数在哈希表中查找单词,如果找到则返回其节点,否则返回NULL。使用insert
函数将单词插入哈希表中。在process_file
函数中,读取每个单词并统计其出现次数,如果单词已经在哈希表中,则将其出现次数加1;否则,将其插入哈希表并设置初始计数为1。在print_hash_table
函数中,将哈希表中的所有单词及其出现次数打印出来。
4.1 哈希冲突
在哈希表中,不同的单词可能映射到同一个哈希值,这种情况称为哈希冲突。一个常用的解决方法是将哈希表中的每个位置都设为一个链表,同一个哈希值的元素都存储在链表中。当查找或插入元素时,先找到对应的表头,然后在链表中查找。
4.2 提高单词匹配的效率
对于较长的单词列表,哈希表的查找和插入操作是非常耗时的,可以通过以下两种方式来提高其效率:
使用平衡树等高效的数据结构代替哈希表。
引入一些启发式算法,构建前缀树等数据结构,以减少查找或插入次数。
5. 总结
通过以上介绍,我们了解到了如何使用C语言统计单词个数的方法,包括如何定义单词、如何读取文本、如何统计单词等。同时,我们也可以从中了解到哈希表以及哈希冲突解决方法。希望本文能够对希望学习或使用C语言进行单词统计的读者有所帮助。