C语言实现中文模糊查询

需求

        在帮人设计一款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

  • 31
    点赞
  • 153
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值