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

  • 1. 因为要写个函数:实现截取指定字节长度的中英混合字符串,当截取位置为半个汉字字符时,舍去当前字节.并要求写入对话框应用程序中.
  • 对话框有很多的坑啊,并且起初我是不知道vs与vc++6.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)


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

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

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

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

  • 一种普遍的说法是:0xa0是汉字编码开始的地方.所以只要判断指定位是否大于0xa0就能判断中英文.(当然,我基于这种判断写了一版函数,测试后发现有问题).了解到,0xa0(160)位置是特殊字符不间断空格.0xa1才是汉字开始的位置(啊),所以判断的时候当然不能大于等于零.
    (当然,这种思路用来判断某个字符是否是双字节字符确实可行,但还是不能解决汉字高低位的问题,且0xa1只是低位字节开始的地方,汉字编码的方式决定了没办法通过二进制编码等方式来区分汉字的高低位)

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;
}
  • 另一种判断字符的思路是取一个有符号的int型变量,如果它小于零.则证明该字节位是一个双字节字符.原理是什么?不论对于多字节字符集(窄字节),还是unicode(宽字节).都是在ASCll码的延伸,其0-127,都正好是西文以及西文符号的编码位.而我们知道0-127的二进制位(0000 0000-0111 1111),它们的有符号整形数都是正的,其所有高位正好都为0,所以双字节字符,最高位必定为1,这在有符号整形中代表负号.当然这种方式确实能判断出当前字节位是否是属于一个双字节字符位.但是还是无法满足截取指定字节长度的混合字符串.
    (当然我基于这种判断机制也写了一版函数,结果当然还是不行,因为更本的原因还是在于汉字编码的高低位无法确定).
// 截取指定长度的字符串
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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350