需求
在帮人设计一款C实现的过期图书管理系统时, 给出了对借书学生姓名进行模糊查询的要求。 也即如果存储信息里有 "马飞飞" 这个人, 我们检索其子串 "马" "马飞" "飞" 等都可以检索出这个词条。从逻辑来看,这个功能很简单,只需要判断 输入串 str 和 目标串 target 之间是否存在串包含关系, 也即 str 是否是 target 的子串。
问题
从思路分析来看, 这个算法应该挺容易实现的,但是你在实现的过程中会发现 中文串 跟 英文串的模糊查询是有一定区别的。其中对英文串的判断, 我们甚至只需要使用 strstr() 函数即可完成。
因为目的是为了判断子串和主串的包含关系, 如果函数返回了一个下标, 我们就可以由此判断输入串是目标串的子串, 也即符合模糊查询的原则,进行输出即可; 如果返回 NULL, 那么我们就可以判断输入串不是目标串的子串,不打印结果。
以上也即模糊查询的主体思路, 但是我们在使用对英文字符的方式进行判断时出现了问题, 也即输入一个中文字符 "汪" 可能会出现 "王三" "王四" "汪五" 的输出结果, 也即多打印了额外结果。
这个问题是由于 中文字符 特殊的存储方式导致的, 一般的英文字符在字符数组中只占 1位, 但是 中文字符 占2位。 因此,我们通过英文字符按一位比较的方式会存在问题。 也即
str[i] == target[i]
是错误的。
如果需要对中文字符进行判断的话, 我们就需要以 二位字符 为窗口在主串上进行滑动, 因为每两位字符代表了 一个字。
代码
以下是我使用的部分代码, 它以全局变量结构体数组 books 的 name 属性作为目标串, 通过输入串 str 和 目标串 books[i].name 的迭代匹配, 得到最终模糊查询的结果。代码设定滑动窗口为2进行比较, 当输入串为多个字时, 进行循环比较, 如果该字在目标串中出现 tag + 1, 如果最终的 tag 等于 输入串的字数就输出。
如:
主串: 马飞飞 target.length = 6 '马' '飞' '飞' 每个字占 2 位
输入串: 马飞 str.length = 4 '马' '飞' 每个字占 2 位
初始: tag = 0
以 2 为窗口大小进行判断
① '马' in "马飞飞" tag++
② '飞' in "马飞飞" tag++
if( tag == str.length / 2) //有2个字, 字符数组长度应该除2再比较。
str 是 target 的子串,输出查询结果
else
str 不是 target 的子串
int vague(char * str,char * target){
//中文占两位
int i,j,tag = 0;
for(i = 0;i < strlen(str);i+=2){
for(j = 0;j < strlen(target);j+=2){
if(str[i] == target[j] && str[i+1] == target[j+1])
tag++;
}
}
return tag == strlen(str) / 2 ? 1 : 0;
}
//模糊查询
void seaBookVague(char str[20]){
int i,j;
for(i = 0; i < bcount; i++){
if(vague(str,books[i].name))
print(books[i]);
}
}
结果
books.txt
运行结果
UPDATE
这篇文章尚是我初学C语言写下的, 可能有很多纰漏, 在如今看来也是有很多问题的, 当时由于才疏学浅没考虑到, 如今再做一次更新。
核心为 cmpChStr() 函数, 这里设置本地环境为中文支持, 因此C语言会自动提供对中文字符的比较支持, 因此也就不需要考虑本文上述的中文字符存储规则了。故在此基础上对输入字符串 source 和目标字符串 target 进行 strstr() 判断(该函数判断source是否是target的子串), 同时如果 source == target 也成立, 这样就完成了模糊查询的功能。
在 main 函数中给出的使用方法
代码如下所示:
#include <stdio.h>
#include <string.h>
#include <locale.h>
// 定义一个用于比较字符串的函数,忽略中文字符的差异
int cmpChStr(char* str1, char* str2) {
//设置当前环境为当前系统的默认环境,以支持中文字符比较
setlocale(LC_ALL, "");
return strcmp(str1, str2);
}
int vagueSearch(char * source,char * target){
if (strstr(target, source) != NULL || cmpChStr(target, source) == 0) {
return 1;
}
return 0;
}
int main() {
//source Str && target Str
char* strings[] = {
"中国",
"中华人民共和国",
"美国",
"日本",
"韩国",
"英国",
"国国"
};
int size = sizeof(strings) / sizeof(strings[0]);
for(int i = 0;i < size;i++){
if(vagueSearch("国", strings[i])){
printf("%s\n", strings[i]);
}
}
return 0;
}
OVER
update in midnight - 2023-9-21 00:08:32