中文按拼音首字母排序的C++实现方案

先介绍下背景,最近有个需求,需要将用户的好友列表按照昵称的拼音首字母排序,类似于手机电话簿的联系人。一开始建议让终端同学去做这点,毕竟终端现有的电话簿模块已经实现了,不过终端同学说电话簿是系统组件,移植出来需要额外开发,导入库等等。建议后台来实现,终端直接接受处理好的数据,于是就由后台来做了。对于这个问题刚开始没有什么思路,不知道怎么着手,问题包括如何识别昵称里的中文、中文如何转换为拼音等等。咨询了组内的大佬,给出建议:首先,中文字符是可以识别出来的,因为我们编码都是统一的utf8编码,utf8是unicode编码的一种实现方式,在unicode编码标准中,中文字符的unicode编码范围是0X4E00~0X9FA5,可以通过检测这一范围来识别中文,这样,第一个问题解决了。其次,中文转拼音,预先准备好一个字典map,存储中文和拼音的对应关系,转换的时候直接读取map。这里中文拼音对应关系其实已有前辈做过,github上有了映射文件,可以直接拿来使用。

两个难题解决了,方案就出来了,分为三部分:首先是拼音map的生成处理,其次是中文的识别以及转换,最后是业务处理。demo的效果图如下:

图1. github上的中文拼音字典格式,链接放在参考文档里

图2. 编译运行截图 

参考代码:

1. 字典解析部分

/* PinyinMapParser.h */

#include <map>
#include <string>

using namespace std;
class PinyinMapParser 
{
public:
    PinyinMapParser(){}
    ~PinyinMapParser(){}

public:
    // 从文件中获取拼音字典
    static int GetPinYinMap(string& path, map<string, string>& pin_yin_map);
};


/* PinyinMapParser.cpp */


#include <iostream>     
#include <fstream>      
#include "PinyinMapParser.h"


// 获取拼音映射文件
int PinyinMapParser::GetPinYinMap(string& path, map<string, string>& pin_yin_map)
{
    // 路径校验
    if (path.empty())
    {
        cout<<"path emtpy, invalid param"<<endl;
        return -1;
    }

    // 读取拼音文件
    std::ifstream is(path.c_str());
    if (!is.is_open())
    {
        cout<<"open file:"<<path<<" error"<<endl;
        return -1;
    }

    while (!is.eof())
    {
        string tmp_pinyin;

        // 每次读取一行,这里拼音文件格式:王=wang1,wang2,数字表示声调
        getline(is, tmp_pinyin);
        //cout<<"getline:"<<tmp_pinyin<<endl;
        if (tmp_pinyin.find("=") != string::npos)
        {
            string zh, pinyin;
            size_t i = tmp_pinyin.find_first_of('=');
            if (i != string::npos && i != tmp_pinyin.size()-1)
            {
                // 发音有多个,我们只取一个
                zh.assign(tmp_pinyin, 0, i);
                pinyin.assign(tmp_pinyin, i+1, tmp_pinyin.size()-i-1);

                // 去掉拼音末尾声调
                if (pinyin.find(",") != string::npos)
                {
                    size_t j = pinyin.find(",");                    
                    pinyin.assign(pinyin, 0, j-1);
                }
                else
                {
                    pinyin.assign(pinyin.begin(), pinyin.end()-1);
                }
                // 取出来后放到字典里
                //cout<<"zh:"<<zh<<", pinyin:"<<pinyin<<endl;
                pin_yin_map[zh] = pinyin;
            }
        }
    }

    // 关闭文件
    is.close();
    return 0;
}

2. 中文转换部分:

/* Convert.h */

#include <map>
#include <string>

using namespace std;
class Convert 
{
public:
    Convert(){}
    ~Convert(){}

public:
    // 昵称转换中文
    static int ConvertNicknameToPinyin(string& nickname, string& convert_str, map<string, string>& pinyin_map);
    // 判断是否是中文
    static bool IsZh(const char* p);
};


/* Convert.cpp */


#include <iostream>
#include <string>
#include "Convert.h"


// 获取拼音映射文件
int Convert::ConvertNicknameToPinyin(string& nickname, string& convert_str, map<string, string>& pinyin_map)
{
    // 检查昵称是否为空
    if (nickname.empty())
    {
        cout<<"nickname is empty, invalid"<<endl;
        return -1;
    }

    {
        size_t i = 0; 
        string tmp_str;

        // 逐个字符检查
        while(i < nickname.length())
        {
            try 
            {
                tmp_str.clear();
                const char *p = &nickname.at(i);
                // 中文占用三字节,utf8中三字节编码第一个字节前四位是1110
                if((*p & 0xF0) == 0xE0 && IsZh(p))
                {                
                    tmp_str.append(&nickname.at(i), 3);             
                    // 从字典里找对应的拼音
                    if (pinyin_map.find(tmp_str) != pinyin_map.end())
                    {
                        convert_str.append(pinyin_map[tmp_str]);
                    }
                    else
                    {
                        cout<<"can't find zh pinyin,zh:"<<tmp_str<<endl;
                        convert_str.append(tmp_str);
                    }
                    i += 3;
                }
                else
                {
                    convert_str.append(p,1);
                    i += 1;
                }
            }
            catch (...)
            {
                cout<<"exception occurs"<<endl;
                return -1;
            }
        }         
    }

    return 0;
}

// 判断是否是中文
bool Convert::IsZh(const char* p)
{
    if ( NULL ==  p)
    {
        cout<<"input param null"<<endl;
        return false;
    }

    // 中文unicode编码范围是0X4E00~0X9FA5,即utf8范围0xe4b880 ~ 0xe9baa5
    // 使用utf8编码占3个字节,下面是分别对三个字节校验,utf8编码介绍 https://blog.csdn.net/zhusongziye/article/details/84261211

    if ((*p&0xF0) == 0xE0)
    {
        if( *(p+1) == '\0' || *(p+2) == '\0')
        {
            return false;
        }
        unsigned char v  = *p;
        unsigned char v1  = *(p+1);
        unsigned char v2  = *(p+2);

        if( (v1&0xC0) != 0x80 || (v2&0xC0) != 0x80 )
        {
            return false;
        }

        if( v < 0xE4)
        {
            return false;
        }
        if( v == 0xE4 && v1 < 0xB8)
        {
            return false;
        }
        if( v == 0xE4 && v1 == 0xB8 && v2 < 0x80)
        {
            return false;
        }

        if(v > 0xE9)
        {
            return false;
        }
        if(v == 0xE9 && v1 > 0xBE)
        {
            return false;
        }
        if(v == 0xE9 && v1 == 0xBE && v2 > 0xB5)
        {
            return false;
        }

        p += 3;

        return true;
    }

    return false;
}

3. 主函数

 #include <iostream>
#include <map>
#include <string>
#include "PinyinMapParser.h"
#include "Convert.h"


using namespace std;

int main (int argc, char** argv)
{
    int ret = 0;
    if (argc < 3)
    {
        cout<<"Usage: ./exe file_path"<<endl;
        return -1;
    }

    string path(argv[1]);
    //cout<<"input param:"<<path<<endl;

    map<string, string> m_pinyin;
    // 解析拼音字典
    ret = PinyinMapParser::GetPinYinMap(path, m_pinyin);
    if (ret != 0)
    {
        cout<<"GetPinYinMap error, ret:"<<ret<<", path:"<<path<<endl;
        return ret;
    }

    string zh(argv[2]);
    string zhpinyin;
    // 拼音转换
    ret = Convert::ConvertNicknameToPinyin(zh, zhpinyin, m_pinyin);
    if (ret != 0)
    {
        cout<<"parse "<<zh<<" error"<<endl;
        return ret;
    }

    cout<<"parse succ, zh:"<<zh<<", pinyin:"<<zhpinyin<<endl;
    return ret;
}

参考资料:

1. 汉字 Unicode 编码范围

2. Unicode 和 UTF-8 关系

3. github 拼音链接

================================================================================================

  • 16
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值