[c/c++]多字节字符集如何判断汉字高位字节与低位字节

2019-12-16  本文已影响0人  葛木小舍先生丶

因为C++支持两种字符串,即常规的ANSI编码(使用""包裹)和Unicode编码(使用L""包裹),这样对应的就有了两套字符串处理函数,比如:strlen和wcslen,分别用于处理两种字符串.

在以前VC++6.0中默认的字符集是多字节字符集(MBCS:Multi-Byte Character Set),而VS2005及以后默认的字符集是Unicode,这样导致以前在VC6.0中非常简单实用的各类字符操作和函数在VS2010环境下运行时会报各种各样的错误。
字符集可以通过工程属性修改:“工程-属性-字符集”。
CString在Unicode和多字节字符集下的区别:CString 是基于 TCHAR 数据类型的。如果为程序的生成定义了符号 _UNICODE,则会将 TCHAR 定义为 wchar_t 类型(一个 16 位的字符编码类型);否则,会将它定义为 char(普通的 8 位字符编码)。于是,在 Unicode 下,CString 由 16 位字符组成。如果没有 Unicode,它们则由 char 类型的字符组成(来自MSDN)。

图片.png

多字节字符集(DBCS :double-byte character set)即以前的窄字节,是基于ASCll码的基础之上扩展而来的,采用多字节(两个字节)
来对各国复杂的字符进行编码.(不同国家有各种不同的编码标准,汉字编码的国家标准有,gbk,gb2132-80,GB18030)


一开始的错误解决思路是:

想着只要通过循环判断字符串每个字节是否属于双字节字符,当其最后一个字节位属于双字节字符的时候,就去掉该字节.(这个思路是错误的,通过简单的判断最后一个字节位是否为双字节字符位是不可行的,汉字时双字节字符,它的两个字节位都满足这个条件,这意味着,当截取字节位恰好属于一个汉字的高字节位时,舍掉它是错误的,当然一开始我并没有意识到这个错误,)

所以我起初将一切思考的核心放在了如何判断当前字节位属于双字节字符(判断某个字符是否为汉字)这个问题上.

于是了解到了两种解决方式:


CString inceptorCS(CString Cstr,int bitnum){

    CString restr;
    int nlen = Cstr.GetLength();

    if(bitnum > Cstr.GetLength()){
        AfxMessageBox(_T("无法截取,截取长度大于字符串长度!"));
    }else if(bitnum == Cstr.GetLength()){
        restr = Cstr.Left(bitnum);
    }
    else{
    // 声明一个无法号char变量来存储截取字节处的字符.
        unsigned char cha1;

        int nl = bitnum;

        cha1 = Cstr[bitnum];

    // 判断截取字节位置的字符大小来分别中文与英文
        if(cha1 > 0xa0){
            restr = Cstr.Left(nl);  
        }else{
            restr = Cstr.Left(nl);
        }
    }   
        return restr;
}
// 截取指定长度的字符串
string interceptString(string str,int n) {

    string restr; 
    if (n > str.length()){
        cout << "无法截取,截取长度大于字符串长度";
    }
    else {
            char a = str[n];
            signed int val = a;
            if (val >= 0){
                restr = str.substr(0,n);                
            }
            else
            {
                restr = str.substr(0, n+1);             
            }
    }
    return restr;
}

正确的解决思路和参考解决方式:

至此,经过一些列的思考和挖坑,入坑.最后才将思路转换到了如何判断双子节汉字的高低位上,最终悲哀的发现汉字双子节高低位并没有明显的界限划分.也没有发现有效的系统函数,或库函数(本来就是要自己实现,就没有去找).最后想着,只能通过循环控制,来人工切分,记录整个混合字符串的状态,从而找到高低位字节.
(经过反复测试,最终写的一版函数,达到了我想要的效果)

void CMFCCStingDlg::OnBnClickedButton1()
{
    CString restr;
    // int nlen = m_string.GetLength();
    // TODO: 在此添加控件通知处理程序代码
    
    UpdateData(TRUE);
    if (m_num > m_string.GetLength()) {
        AfxMessageBox("截取长度过长");
    }
    else {
        int i = 0;
       // 取到要截取位置的字节下标.
        int byteval = m_num - 1; 
      // 通过while循环来遍历整个字符串.
        while (i <byteval){
      // 判断其是否属于双字节字符.是则跳过其一字节.否则自增1.
            if (m_string[i]<0){
                i += 2;
            }
            else {
                i += 1;
            }
        }

        if (i>byteval) { // 满足该条件说明,当前字节位为双字节字符的低字节位.
        // CString.Left截取方法与传统方法不同,其下标是以1为起始,按字节截取字符串,当然如果你使用unicode编码的话,就直接按字符截取了.
            restr = m_string.Left(m_num);
        }
        else if(m_string[i]<0){ // 满足该条件,证明该字节位置为一个双字节字符的高字节位.
            restr = m_string.Left(m_num-1);
        }
        else { // 剩余的都是西文.0-127范围内.
            restr = m_string.Left(m_num);
        }
    }
    AfxMessageBox(restr);
}
图片.png

需要注意的一点是,对话框程序,edit 编辑框显示数据会进行自动处理掉半个双字节字符(这也是很坑的一点,更本没必要自己实现,一开始还以为自己写的函数没问题了),通过messagebox弹出框函数可以完整弹出截取的字符串内容,即便是半个双字节字符.


2019.12.21.
21:54

上一篇下一篇

猜你喜欢

热点阅读