一款高性能敏感词(非法词/脏字)检测过滤组件,附带繁体简体互换,支持全角半角互换,汉字转拼音,模糊搜索等功能。

Overview

ToolGood.Words

一款高性能非法词(敏感词)检测组件,附带繁体简体互换,支持全角半角互换,获取拼音首字母,获取拼音字母,拼音模糊搜索等功能。

C#语言,使用StringSearchEx2.Replace过滤,在48k敏感词库上的过滤速度超过3亿字符每秒。(cpu i7 8750h)

csharp 文件夹说明:

ToolGood.Pinyin.Build:          生成词的拼音
ToolGood.Pinyin.Pretreatment:   生成拼音预处理,核对拼音,词组最小化
ToolGood.Transformation.Build: 生成简体繁体转换文档,更新时文档放在同一目录下,词库参考 https://github.com/BYVoid/OpenCC
ToolGood.Words.Contrast:        字符串搜索对比
ToolGood.Words.Test:            单元测试
ToolGood.Words:                 本项目源代码

非法词(敏感词)检测(字符串搜索)

非法词(敏感词)检测类:StringSearchStringSearchExStringSearchEx2WordsSearchWordsSearchExWordsSearchEx2IllegalWordsSearch;

  • StringSearchStringSearchExStringSearchEx2StringSearchEx3: 搜索FindFirst方法返回结果为string类型。
  • WordsSearchWordsSearchExWordsSearchEx2WordsSearchEx3: 搜索FindFirst方法返回结果为WordsSearchResult类型, WordsSearchResult不仅仅有关键字,还有关键字的开始位置、结束位置,关键字序号等。
  • IllegalWordsSearch: 过滤非法词(敏感词)专用类,可设置跳字长度,默认全角转半角,忽略大小写,跳词,重复词,黑名单, 搜索FindFirst方法返回为IllegalWordsSearchResult,有关键字,对应原文,开始、位置,黑名单类型。
  • IllegalWordsSearchStringSearchExStringSearchEx2WordsSearchExWordsSearchEx2 使用SaveLoad方法,可以加快初始化。
  • 共同方法有:SetKeywordsContainsAnyFindFirstFindAllReplace
  • IllegalWordsSearch独有方法:SetSkipWords(设置跳词)、SetBlacklist(设置黑名单)。
  • IllegalWordsSearch字段UseIgnoreCase:设置是忽略否大小写,必须在SetKeywords方法之前,注:使用Load方法则该字段无效。
  • StringSearchEx3WordsSearchEx3为指针版优化版,实测时发现性能浮动比较大。
    string s = "中国|国人|zg人";
    string test = "我是中国人";

    StringSearch iwords = new StringSearch();
    iwords.SetKeywords(s.Split('|'));
    
    var b = iwords.ContainsAny(test);
    Assert.AreEqual(true, b);

    var f = iwords.FindFirst(test);
    Assert.AreEqual("中国", f);

    var all = iwords.FindAll(test);
    Assert.AreEqual("中国", all[0]);
    Assert.AreEqual("国人", all[1]);
    Assert.AreEqual(2, all.Count);

    var str = iwords.Replace(test, '*');
    Assert.AreEqual("我是***", str);

非法词(敏感词)检测(字符串搜索)(支持通配符)

非法词(敏感词)检测类:StringMatchStringMatchExWordsMatchWordsMatchEx

支持部分正则表达式类型:.(点)?(问号) [](方括号) (|)(括号与竖线)

    string s = ".[中美]国|国人|zg人";
    string test = "我是中国人";

    WordsMatch wordsSearch = new WordsMatch();
    wordsSearch.SetKeywords(s.Split('|'));

    var b = wordsSearch.ContainsAny(test);
    Assert.AreEqual(true, b);

    var f = wordsSearch.FindFirst(test);
    Assert.AreEqual("是中国", f.Keyword);

    var alls = wordsSearch.FindAll(test);
    Assert.AreEqual("是中国", alls[0].Keyword);
    Assert.AreEqual(".[中美]国", alls[0].MatchKeyword);
    Assert.AreEqual(1, alls[0].Start);
    Assert.AreEqual(3, alls[0].End);
    Assert.AreEqual(0, alls[0].Index);//返回索引Index,默认从0开始
    Assert.AreEqual("国人", alls[1].Keyword);
    Assert.AreEqual(2, alls.Count);

    var t = wordsSearch.Replace(test, '*');
    Assert.AreEqual("我****", t);

繁体简体互换、全角半角互换、数字转成中文大写、拼音操作

    // 转成简体
    WordsHelper.ToSimplifiedChinese("我愛中國");
    WordsHelper.ToSimplifiedChinese("我愛中國",1);// 港澳繁体 转 简体
    WordsHelper.ToSimplifiedChinese("我愛中國",2);// 台湾正体 转 简体
    // 转成繁体
    WordsHelper.ToTraditionalChinese("我爱中国");
    WordsHelper.ToTraditionalChinese("我爱中国",1);// 简体 转 港澳繁体
    WordsHelper.ToTraditionalChinese("我爱中国",2);// 简体 转 台湾正体
    // 转成全角
    WordsHelper.ToSBC("abcABC123");
    // 转成半角
    WordsHelper.ToDBC("abcABC123");
    // 数字转成中文大写
    WordsHelper.ToChineseRMB(12345678901.12);
    // 中文转成数字
    WordsHelper.ToNumber("壹佰贰拾叁亿肆仟伍佰陆拾柒万捌仟玖佰零壹元壹角贰分");
    // 获取全拼
    WordsHelper.GetPinyin("我爱中国");//WoAiZhongGuo   
    WordsHelper.GetPinyin("我爱中国",",");//Wo,Ai,Zhong,Guo   
    WordsHelper.GetPinyin("我爱中国",true);//WǒÀiZhōngGuó

    // 获取首字母
    WordsHelper.GetFirstPinyin("我爱中国");//WAZG
    // 获取全部拼音
    WordsHelper.GetAllPinyin('传');//Chuan,Zhuan
    // 获取姓名
    WordsHelper.GetPinyinForName("单一一")//ShanYiYi
    WordsHelper.GetPinyinForName("单一一",",")//Shan,Yi,Yi
    WordsHelper.GetPinyinForName("单一一",true)//ShànYīYī
拼音分支

ToolGood.Words.Pinyin 追求更快的加载速度(目前只有C#代码)。

拼音匹配

PinyinMatch:方法有SetKeywordsSetIndexsFindFindIndex

PinyinMatch:方法有SetKeywordsFuncSetPinyinFuncSetPinyinSplitCharFind

    string s = "北京|天津|河北|辽宁|吉林|黑龙江|山东|江苏|上海|浙江|安徽|福建|江西|广东|广西|海南|河南|湖南|湖北|山西|内蒙古|宁夏|青海|陕西|甘肃|新疆|四川|贵州|云南|重庆|西藏|香港|澳门|台湾";

    PinyinMatch match = new PinyinMatch();
    match.SetKeywords(s.Split('|').ToList());

    var all = match.Find("BJ");
    Assert.AreEqual("北京", all[0]);
    Assert.AreEqual(1, all.Count);

    all = match.Find("北J");
    Assert.AreEqual("北京", all[0]);
    Assert.AreEqual(1, all.Count);

    all = match.Find("北Ji");
    Assert.AreEqual("北京", all[0]);
    Assert.AreEqual(1, all.Count);

    all = match.Find("S");
    Assert.AreEqual("山东", all[0]);
    Assert.AreEqual("江苏", all[1]);

    var all2 = match.FindIndex("BJ");
    Assert.AreEqual(0, all2[0]);
    Assert.AreEqual(1, all.Count);

性能对比

执行10万次性能对比,结果如下:

10W次性能对比

注:C#自带正则很慢,StringSearchEx2.ContainsAnyRegex.IsMatch效率的8.8万倍多,跟关键字数量有关。

Regex.Matches的运行方式跟IQueryable的类似,只返回MatchCollection,还没有计算。

在 Find All测试中,

FastFilter只能检测出7个: [0]: "主席" [1]: "赵洪祝" [2]: "中国" [3]: "铁道部" [4]: "党" [5]: "胡锦涛" [6]: "倒台"

StringSearch检测出14个: [0]: "党" [1]: "党委" [2]: "西藏" [3]: "党" [4]: "党委" [5]: "主席" [6]: "赵洪祝" [7]: "中国" [8]: "铁道部" [9]: "党" [10]: "胡锦涛" [11]: "锦涛" [12]: "倒台" [13]: "黑社会"

插曲:在细查Regex.Matches神奇3ms,我发现Regex.Matches有一个小问题,

Regex.Matches只能检测出11个: [0]: "党" [1]: "西藏" [2]: "党" [3]: "主席" [4]: "赵洪祝" [5]: "中国" [6]: "铁道部" [7]: "党" [8]: "胡锦涛" [9]: "倒台" [10]: "黑社会"

猜想

非法词(敏感词)检测方法可用于电脑病毒检测及基因检测。

个人未来规划

ToolGood.Algorithm项目

1、完成javascript版本,目前 javascript只写了一小部分。

ToolGood.Words项目

1、GO语言算法优化:在GO版本移植成功后,测试后性能不理想,性能 GO < JAVA < C#,因为我没有完全理解GO语言。JAVA与C#采用了新的算法,GO还是采用老的算法。

2、汉字转拼音功能扩展:当初为了减少dll体积,删去古诗拼音,准备添加一个加载拼音库的功能,以及拼音库管理器。

3、繁体简体转换功能扩展:https://github.com/BYVoid/OpenCC 项目更新比较频繁,过于频繁更新dll库,对项目更新不太好友。准备添加一个添加繁体简体字典的功能,及字典生成器。

4、代码添加注释:AC自动机原理本身比较难理解,优化后的代码更难理解了。

其他方向:

1、研究一下《多个关键字组合过滤》技术。

2、研究一下《情感分析》技术

3、整理一份敏感词。

细分敏感词词组:动词+名词+特定词

细分敏感词分类:涉政文本、涉爆文本、色情文本、辱骂文本、非法交易文本、广告导流文本

探讨敏感信息过滤:

我建一个Q群,用于探讨敏感信息过滤算法、敏感词汇及图片过滤。

群文件分享了一些收集的资料:通用敏感词、购物app、聊天app、游戏app敏感词。

敏感信息过滤研究会,Q群:128994346

Comments
  • java版IllegalWordsSearch部分敏感词搜索不到

    java版IllegalWordsSearch部分敏感词搜索不到

    • 部分敏感词搜不到,不知道是不是量词的关系
                    String test = "一,二二,三三三,四四四四,五五五五五,六六六六六六";
    		List<String> list = new ArrayList<String>();
    		list.add("一");
    		list.add("二二");
    		list.add("三三三");
    		list.add("四四四四");
    		list.add("五五五五五");
    		list.add("六六六六六六");
    		System.out.println("IllegalWordsSearch run Test.");
    
    		IllegalWordsSearch iwords = new IllegalWordsSearch();
    		iwords.SetKeywords(list);
    
    		boolean b = iwords.ContainsAny(test);
    		if (b == false) {
    			System.out.println("ContainsAny is Error.");
    		}
    
    		IllegalWordsSearchResult f = iwords.FindFirst(test);
    		if (f.Keyword.equals("一") == false) {
    			System.out.println("FindFirst is Error.");
    		}
    
    		List<IllegalWordsSearchResult> all = iwords.FindAll(test);
    		if (all.get(0).Keyword.equals("一") == false) {
    			System.out.println("FindAll is Error.");
    		}
    		if (all.get(1).Keyword.equals("二二") == false) {
    			System.out.println("FindAll is Error.");
    		}
    		if (all.get(2).Keyword.equals("三三三") == false) {
    			System.out.println("FindAll is Error.");
    		}
    		if (all.get(3).Keyword.equals("四四四四") == false) {
    			System.out.println("FindAll is Error.");
    		}
    		if (all.get(4).Keyword.equals("五五五五五") == false) {
    			System.out.println("FindAll is Error.");
    		}
    		if (all.get(5).Keyword.equals("六六六六六六") == false) {
    			System.out.println("FindAll is Error.");
    		}
    
    • 敏感词找不到
                    String test = "jameson吃饭";
    		List<String> list = new ArrayList<String>();
    		list.add("jameson吃饭");
    		list.add("吃饭jameson");
    		System.out.println("IllegalWordsSearch run Test.");
    
    		IllegalWordsSearch iwords = new IllegalWordsSearch();
    		iwords.SetKeywords(list);
    
    		boolean b = iwords.ContainsAny(test);
    		if (b == false) {
    			System.out.println("ContainsAny is Error.");
    		}
    
    		IllegalWordsSearchResult f = iwords.FindFirst(test);
    		if (f.Keyword.equals("jameson吃饭") == false) {
    			System.out.println("FindFirst is Error.");
    		}
    
    • WordsSearchEx、WordsSearchEx2、StringSearchEx、StringSearchEx2也有部分词无法搜索
    opened by inazuma1 10
  • 词典里有的关键词识别不出来

    词典里有的关键词识别不出来

        IllegalWordsSearch searcher = new IllegalWordsSearch();
        List<String> list = new ArrayList<>();
        try (Stream<String> stream = Files.lines(ResourceUtils.getFile("classpath:dict/SensitiveDict.txt").toPath(), StandardCharsets.UTF_8)) {
            stream.forEach( word ->{
                list.add(word);
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
        searcher.SetKeywords(list);
    
        List<IllegalWordsSearchResult> words = searcher.FindAll(content);
        words.forEach(word -> {
            System.out.println(word.Keyword + " - " + word.MatchKeyword);
        });
    

    测试代码如上,在SensitiveDict中的许多关键词,在句子中却识别不出来。 目前SensitiveDict词典的大小在3000个词条左右。 如果SensitiveDict就放几条测试词条,就能够识别出来,但到3000个左右就识别不了,请问大佬是什么情况呢...

    opened by Sugarsugarzz 7
  • IllegalWordsSearch 无法识别数字敏感字?

    IllegalWordsSearch 无法识别数字敏感字?

    ` string text = "A1002481003吃饭"; var keywords = new string[] { "248" }; var iws = new IllegalWordsSearch(); var ss = new StringSearch(); var sse = new StringSearchEx(); iws.SetKeywords(keywords);

        //无结果
        var iwsFirst = iws.FindFirst(text);
        var iwsAll = iws.FindAll(text);
    
        //正常识别出248
        ss.SetKeywords(keywords);
        var ssFirst = ss.FindFirst(text);
        var ssAll = ss.FindAll(text);
        
        //正常识别出248
        sse.SetKeywords(keywords);
        var sseFirst = sse.FindFirst(text);
        var sseAll = sse.FindAll(text);
    

    ` 1 2 3

    opened by ijustkeepmoving 6
  • 部分关键词匹配不上问题

    部分关键词匹配不上问题

    你好,麻烦看一下,测试代码中的待检查词,可以在词库中找到,但是无法检查出来 词库: https://github.com/elulis/sensitive-words/blob/master/src/main/resources/sensi_words.txt

    测试代码:

    public static void main(String[] args) {
        long l1 = System.currentTimeMillis();
        IllegalWordsSearch search = new IllegalWordsSearch();
        long l2 = System.currentTimeMillis();
        System.out.println("IllegalWordsSearch init time:" + (l2 - l1));
    
        List<String> list = new ArrayList<>();
        try (Stream<String> stream = Files.lines(ResourceUtils.getFile("classpath:sensi_words.txt").toPath(), StandardCharsets.UTF_8)) {
            stream.forEach( word ->{
                list.add(word);
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
        search.SetKeywords(list);
        long l3 = System.currentTimeMillis();
        System.out.println("load word time:" + (l3 - l2));
    
        String test = "机机歪歪";
    
        boolean b = search.ContainsAny(test);
        if (b) {
            System.out.println("包含非法字符");
        }
        long l4 = System.currentTimeMillis();
        System.out.println("check time:" + (l4 - l3));
    }
    
    opened by inlovingrest 5
  • 大佬,导入字典数据,检索,报错

    大佬,导入字典数据,检索,报错

    代码1如下: var search = new PinyinMatch(); search.SetKeywords(keywords); search.Find("野huo");

    代码2如下: var search = new StringSearchEx3(); search.SetKeywords(keywords); search.FindAll("野火");

    keywords来自字典文件:数据量:351121 https://res.lims.ltd/dict.txt

    两段代码报一样的错: Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object. at ToolGood.Words.internals.PinyinDict.GetPinyinList(String text, Int32 tone) at ToolGood.Words.PinyinMatch.SetKeywords(ICollection`1 keywords)

    opened by ulyanovsk 5
  • 英文使用IllegalWordsSearch 匹配,replace替换不上,

    英文使用IllegalWordsSearch 匹配,replace替换不上,

    var search = new IllegalWordsSearch(); List keywords = new List(); keywords.Add("fuck"); keywords.Add("ffx"); search.SetKeywords(keywords); var result = search.Replace("fFuck");

    bug 
    opened by mshandle 4
  • PinYinSearch大量占用内存有什么好的方案

    PinYinSearch大量占用内存有什么好的方案

    我实际测了一下,使用了67万关键字,12M文本,SetKeywords,2秒左右,占用内存1.7G,这还不算多大的数据情况下,有点招不住,另外,看到FirstPinYinNode=>PinYinNode=>ChineseNode,也就是说树的深度随着指数增加,在大量数据的情况假设按管件字20字,深度60,每三级节点,第一级26字母,第二级,常见拼音406个,第三级,中文字符20903个,这样下来,搜索的深度和广度是否太大了一些,是否有更好的解决方案,能否在设计上做一些优化。

    opened by agilelab 4
  • 发现一个匹配bug

    发现一个匹配bug

    第一个问题是匹配位置的问题: 在 WordsSearch.cs FindAll 方法中 list.Add(new WordsSearchResult(keyword, i + 1 - keyword.Length, i, item)); 这样计算出来的位置有很大偏差 经过我仔细计算后,应该是这样的

    int start = i + 1 - keyword.Length;
    list.Add(new WordsSearchResult(keyword, start, start + keyword.Length, item));
    

    其中 FindFirst 也是一样的问题。

    第二个问题还是匹配位置的问题: 如果匹配的内容中有 \r、\n 这类的字符,那么实际计算出来的位置要减去这类字符的数量

    比如我有如下测试文本:

    庭审中,邱吉林承认了曾同小小发生性关系,但否认知道小小的年龄,其辩护人为他做了轻罪辩护。
    12月16日,备受关注的成都市双流区14岁未成年女生小小(化名)遭遇强奸后跳楼事件在双流区人民法院开庭审理。双流区人民检察院指控,2019年8月至12月期间,被告人邱吉林在明知小小不满14周岁的情况下,采取金钱利诱、威胁等手段,与其相继发生8次性关系,期间还两次拍摄性侵视频并传播。检方认为,被告人邱吉林多次奸淫不满十四周岁的幼女,并造成被害人怀孕等后果,情节特别恶劣,罪行极其严重,应当以强奸罪追究其刑事责任。
    上游新闻记者了解到,被告人邱吉林当庭承认了自己曾与小小发生过性关系,但否认知道其具体年龄,代理律师为他做了轻罪辩护。
    被害人小小的母亲表示,不接受邱吉林的任何道歉与赔偿,希望法院能够对其重判。
    取保候审期间,邱吉林发布的出行照片。图片来源 / 网页截图
    此前,上游新闻刊发的《成都14岁女生被强奸引产后自杀,涉案企业老板取保候审后疑似西藏旅游》《成都少女被强奸后自杀 媒体报道3天后嫌犯被批捕》报道显示,小小的母亲朱琴华表示,女儿生前曾遭企业老板邱吉林的伤害:邱吉林先通过利诱小小发送裸照,再利用裸照胁迫小小发生关系并导致小小怀孕。2月3日,小小发现自己怀孕,到医院检查后发现已怀孕15周。2月4日,成都市公安局双流区分局就小小被强奸一案正式立案侦查,邱吉林被取保候审。成都市公安局双流区分局5月8日作出的鉴定意见书显示,“邱吉林是小小引产胎儿的生物学父亲”。
    6月28日晚,小小在位于双流区的家中结束了自己的生命。
    今年7月2日,媒体广泛报道了小小的遭遇后,双流警方将已经取保候审近5个月的邱吉林刑事拘留,7月16日邱吉林被逮捕,10月30日检方移送法院起诉。
    双流区人民检察院指控,2019年8月20日,邱吉林与被害人小小相互添加为QQ好友并进行聊天。2019年8月至12月期间,邱吉林林在明知小小不满14周岁的情况下,采取金钱利诱、威胁等手段,与其相继发生8次性关系。
    上游新闻记者注意到,邱吉林在8次性侵小小时,均选择了双流区的酒店进行犯罪。
    检方指控,邱吉林除了性侵小小外,还拍摄了小小的裸照,记录了侵犯过程,还将这些裸照和视频两次发送给朋友进行炫耀。
    检方认定,邱吉林在2019年10月19日性侵小小时,还拍摄了她手机通话记录的页面,其中包括小小母亲和其他亲属的电话号码。
    小小的母亲朱琴华对上游新闻记者表示,邱吉林不仅通过拍摄小小裸照以传给她朋友的方式相威胁,还威胁将这些事告诉其家人,“邱吉林记录我和其他亲戚电话号码的目的,就是为了威胁小小不要把这些事告诉我们,以达到控制小小的目的。”
    鉴定书证实,邱吉林是小小引产胎儿的父亲。图片来源 / 受访者提供
    庭审中,邱吉林承认了曾同小小发生性关系,但否认知道小小的年龄,其辩护人为他做了轻罪辩护。
    检方在庭审中指出,邱吉林曾询问过小小的同学小小的年龄,同时根据邱吉林和小小同学聊天记录也可以认定,邱吉林知道或者应当知道小小未成年。
    朱琴华告诉上游新闻记者,邱吉林曾不止一次地通过聊天软件添加过小小同学,“还曾把小小的裸照发给她的同学,不止一次询问小小的年龄。”
    上游新闻记者获悉,小小母亲朱琴华以刑事附带民事原告人的身份,向法院提出了民事赔偿请求,要求邱吉林赔偿医疗费、护理费等共计83万余元。
    12月16日庭审结束后,双流区人民法院未对此案当庭宣判。
    小小的母亲朱琴华告诉上游新闻记者,获悉邱吉林曾向朋友炫耀性侵过未成年少女、发送小小裸照给其他人之后十分气愤,“我唯一的要求就是法院对他重判。”朱琴华称,小小被性侵一案审理结束后,她也希望能够搞清楚为何邱吉林在被以强奸罪立案侦查后,可以在当天取保候审,取保候审期间还可以在全国范围内自由行动,警方为何没有在DNA报告出来后对其变更刑事强制措施,“女儿生前不仅一次问过我,为什么不及时抓邱吉林?这也是我的疑问。”
    上游新闻记者 胡磊
    

    以 “强奸” 这个词为例

    正常匹配出来的开始位置应该是 80,结束位置是82 但实际结果是:79,81 问题是因为在 其辩护人为他做了轻罪辩护。 这句后面有个 \n 符号

    以上两个结果是基于第一问题我改了代码后匹配出来的位置。

    opened by lantongxue 3
  • 能不能在wiki里添加每个API支持的字符集?

    能不能在wiki里添加每个API支持的字符集?

    不错的工具,做项目正好用到.我看代码里面现在是这样写的

            /// <summary>
            /// 获取单字拼音
            /// </summary>
            /// <param name="ch"></param>
            /// <returns></returns>
            public static String Get(Char ch)
            {
                if (ch >= 0x4e00 && ch <= 0x9faf) {
    
    

    能添加一个具体说明注明每个API已经支持的字符集和计划支持哪些就更好了

    opened by ZUOXIANGE 3
  • 有的偏僻汉字不能识别出拼音

    有的偏僻汉字不能识别出拼音

    谢谢您的软件,在多音字识别方面超出了很多同类软件。不过还有的偏僻汉字不能识别出拼音,比如这几个字:“㘄䉄䬋䮚䚏㭁䖆”,望改进。 "㘄": "lēng", "䉄": "léng", "䬋": "léng", "䮚": "lèng", "䚏": "lèng,lì,lìn", "㭁": "réng", "䖆": "niàng"

    opened by armyhg 3
  • golang版本的BaseSearchEx sortMap函数没有起到排序的作用

    golang版本的BaseSearchEx sortMap函数没有起到排序的作用

    func (s *BaseSearchEx) sortMap(mp map[int32]int) []int32 { var newMp = make([]int, 0) var newMpKey = make([]int32, 0) for oldk, v := range mp { newMp = append(newMp, v) newMpKey = append(newMpKey, oldk) } sort.Ints(newMp) //排序

    list := make([]int32, 0)
    for k, _ := range newMp { //这里的k是下标是0到len(newMp)-1,也就是跟for k:=0;k<len(newMp);k++ {一样,上面排序没有用 
    	list = append(list, newMpKey[k])
    }
    return list
    

    }

    opened by youminxue 0
Releases(3.1.0.0)
Owner
ToolGood
I love C# jq mysql......
ToolGood
汉字转拼音(pypinyin)

汉字拼音转换工具(Python 版) 将汉字转为拼音。可以用于汉字注音、排序、检索(Russian translation) 。 基于 hotoo/pinyin 开发。 Documentation: http://pypinyin.rtfd.io/ GitHub: https://github.co

Huang Huang 4.2k Jan 3, 2023