时至今日,字符串使用unicode已经是不需要理由的常识,但对一些有着悠久历史的编程语言来说,这仍然是个头痛的问题。如果抛开第三方库的支持,C++其实并不能实际有效地支持unicode,即使是utf8。(注:本文讨论的是字符串在内存中的编码方案,而不是文件或网络数据流。)

STL的string模板诞生时,unicode还是理想中的固定16位编码。那时,Windows、Java等先后跨跃进unicode时代,而Unix/Linux受限于向后兼容而难以改变。那时Windows上的C++编程主要用用Win32 API,还不流行STL,而Unix/Linux上还基本不支持Unicode。STL的wstring,只是将char模板参数替换成wchar_t,看起来似乎完全合理,其实并没有经过实践检验。所以,Windows上的wstring至今一直处于实际上不可用的状态,各种IO时的编码转换都有问题;而Linux上的wchar_t是32位,太浪费内存所以完全不值得使用。(最新的标准针对unicode引入了char16_t和char32_t,以及u16string和u32string

为什么Linux上的wchar_t是32位呢?因为gcc开始支持宽字符的时候,大约也是unicode的字符集突破16位编码极限的时候。本来预期非常充足的码位,出乎意料的不够了,unicode不得不做出调整。调整后,有了三种可选的unicode编码:
    utf32:一个码位固定4字节,一个码位表示一个字符编码。
    utf16:兼容之前的16位编码,一个码位2字节,但1或2个码位表示一个字符编码(所以有可能会有问题)。
    utf8:兼容ASCII编码,一个码位1字节,但1到6个码位表示一个字符编码。(现行标准其实要求最多4个码位,但出于保障兼容性的原因,5、6个码位的情况是可能出现的,即使这算无效编码。)
    (除此之外,还有字符组合和修饰符组合的情况,使得码位到字符的映射更加复杂,在此忽略。)
    
utf8的出现,让Linux系统发现了新的机会,既然兼容ASCII,那么只要系统的编码页新加一个utf8的,不就支持unicode了么。很自然的,Linux就走了这条路。而Windows却不支持utf8的编码页,这让很多写跨平台程序的人对微软很是不满。而后,更有人宣称,Windows、Java、.NET等选错了unicode编码,utf8才是王道。

我很希望字符编码有个完美的解决方案,但很可惜,每当我们试图让它变得简单,它就变得更加复杂。最基本的问题:一个utf8编码的char数组,只不过是一个字节buffer,在C/C++的标准库支持下,你根本无法把它当字符串来处理。想要字符串长度?它只能给你字节数,而不是字符数。想要取第n个字符?它只能给你第n个字节。想要把一个字符转大写?想判断一个字符是否是字母?它只接受char类型的字符,也就是说只支持ASCII字符……

即使说对一个不需要操作字符的程序,在C/C++程序里我们总是要为输入分配buffer的,那么预期输入n个字符的buffer,应该分配多少字节呢?没错,你只能按最大可能n*6+1来分配。对内存临时分配还好,要是一个数据库字段,你怎么办?这时即使是utf32都可能更省存储空间。

当然,绝大多数问题都可以通过使用一个第三方的unicode库来解决。如果觉得专门的unicode库太heavy,至少还有boost的日常解决方案,只要把所有字符串操作替换成专门的函数……(如果安全性对你的程序很重要,你还要了解所用函数在遇到无效编码时的行为,因为这也是黑客突破安全检查的一种手段。)

但是,大多数的程序员甚至并不清楚unicode编码,更不可能了解编码转换的复杂性,他们或者习惯了C风格的字节操作,或者来自Java等使用双字节unicode的语言,做着并非字处理的软件,所以对学习复杂的unicode编码系统并无太大兴趣。所以,utf8在C/C++里,更象是专家级的解决方案,而并非面向普通开发者。理想情况下,C++可以凭借其强大的抽象和封装能力,包装出一个真正基于字符访问的字符串类(Python3走了这条路,但有很多批评的声音),然而现实中则很难将其标准化。

那么Windows、Java、.NET、iOS等所使用的utf16呢?本质上,他们的支持都是有缺陷的。因为他们开始时支持的其实是utf16的前身UCS2,每个字符固定2字节,而当utf16出现后,就权当UCS2用,也就是说双码位4字节的字符(unicode里称作BMP以外的字符)会被当作两个字符处理。如果你的程序真的想要正确支持双码位的字符,就要改写程序,使用高级字符串函数来访问字符串,而不是直接用下标索引。只是因为BMP以外的字符极其罕用,其程序员并不急需了解这些细节。从正确性角度来说,这并不正确,但从实用角度来说,还算实用。

http://blog.csdn.net/nightmare/article/details/39780931#comments

C++的标准库函数默认都是操作字节,而不是字符,非常痛苦,所以引入了u16string和u32string(Linux上的wchar_t是32位的原因,utf16对unicode的支持是有缺陷的)good的更多相关文章

  1. wchar_t是内置还是别名(亲测有效:wchar_t在windows下是16位整数的别名,在linux等平台下是32位整数的别名。MSVC2008开始默认是/Zc:wchar_t)

    接前一篇C++ ABI之名字改编(以Qt为例),继续看看C++名字改编相关的问题. 问题 MSVC 有一对选项/Zc:wchar_t- 与 /Zc:wchar_t控制wchar_t 于是 wchar_ ...

  2. 是否升级IOS11?IOS11不支持32位程序 查看手机哪些APP不支持

    查看苹果32位APP具体步骤:设置-通用-关于本机-应用程序.如果手机中下载了32位应用的话,苹果会给出应用兼容性提醒:如果手机里没有安装32位应用,右侧没有小三角,点击“应用程序”也会没有反应. I ...

  3. C89标准库函数手册(待整理)

    http://zh.cppreference.com/w/c 前言 ANSI C(C89)标准库函数共有15个头文件.这15个头文件分别为: 1.<assert.h>            ...

  4. 设置联想键盘恢复F1~F12默认按键的操作办法

    背景 默认都是笔记本键盘才有Fn组合功能键,台式机很少有.今天领到的是联想键盘,给我的台式机使用后F12很麻烦,必须Fn+F12才可以. 需求 恢复默认的F1~F12功能 方案 只需要下载驱动安装: ...

  5. C89标准库函数手册

    http://zh.cppreference.com/w/c 前言 ANSI C(C89)标准库函数共有15个头文件.这15个头文件分别为: 1.<assert.h>            ...

  6. 范围for语句 && 列表初始值&& 标准库函数begin和end

    范围for语句: 引入的意义:简化传统for的编写,主要用于遍历给定序列中的每个元素并对序列中的每个值执行某种操作,其语法形式是: for( 声明: 给定序列) { 执行的操作. } 其中,“给定序列 ...

  7. C/C++语言的标准库函数malloc/free与运算符new/delete的区别

    概括地说 1.malloc与free是C++/C的标准库函数,new/delete是C++的运算符,它们都可用于申请动态内存和释放内存. 2.对于非内部数据类型的对象而言,只用malloc/free无 ...

  8. C++/C语言的标准库函数与运算符的区别new/delete malloc/free

    malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符.它们都可用于申请动态内存和释放内存.下面来看他们的区别. 一.操作对象有所不同 malloc与free是C++ ...

  9. C++ - 常用的标准库函数

      写在前面 C++是一门博大精深的语言,也是最难学的一门编程语言,每一位励志学好C++的程序员都需要从基本功开始,稳扎稳打. 自从1998年C++ standard定案以后,C++程序库便有了大幅扩 ...

随机推荐

  1. Android官方教程翻译(6)——添加ActionBar

    The action bar allows you to add buttons for the most important action items relating to the app's c ...

  2. Hamcrest 总结

    Junit JUnit框架用一组assert方法封装了一些常用的断言.这些assert方法可以帮我们简化单元测试的编写.这样的话,Junit就可以根据这些断言是否抛出 AssertionFailedE ...

  3. Android 开发新方向 Android Wear ——概述

    2014 谷歌 I/O大会正式公布的Android Wear 开发理念,从而能够更系统的提供开发人员使用Android接口开发便携式可穿戴设备,以智能手表为例,通过Android提供的接口,能够方便的 ...

  4. 我眼中的robot framework

    由于近期公司需要,需要一个测试框架对于公司的服务做自动化测试. 由于服务的复杂性,人工测试的方式越来越复杂,体现在以下方面: 1.人工测试步骤复杂,容易出错.服务的复杂性会使人工测试的准备工作,测试条 ...

  5. uwp - 解决使用EntityFramework时报错“unable to load dll 'sqlite3':the specified module could not be found”

    在使用uwp的ef过程中碰到一万个问题快折腾死我了,好在最后终于解决掉所有问题,但愿如此,因为在这之前先后发生不同的报错,不知道后面还会碰到新的问题不. 其中一个问题是这样的,生成能正常生成,但是启动 ...

  6. C++:怎样把一个int转成4个字节?

    大家都知道,一个int 或 unsigned int是由4个字节组成的,(<C/C++学习指南>,第3章,第3.2.3节:变量的内存视图) 比如, int   n  =  sizeof( ...

  7. ISO9000 质量管理和质量保证系列国际标准

    (一)ISO9000认证的背景国际标准化组织(ISO)是世界上最主要的非政府间国际标准化机构,成立于二次世界大战以后,总部位于瑞士日内瓦.该组织的目的是在世界范围内促进标准化及有关工作的发展,以利于国 ...

  8. 【HLSL学习笔记】WPF Shader Effect Library算法解读之[DirectionalBlur]

    原文:[HLSL学习笔记]WPF Shader Effect Library算法解读之[DirectionalBlur] 方位模糊是一个按照指定角度循环位移并叠加纹理,最后平均颜色值并输出的一种特效. ...

  9. 在Eclipse安装ADT

    Eclipse 3.4 (Ganymede) 启动 Eclipse,然后选择 Help > Software Updates….在出现的对话框中.单击 Available Software 选项 ...

  10. 脚本 启动/停止 jar包服务

    windows (.bat): @set port=8692 @echo %port% for /f "tokens=5" %%i in ('netstat -aon ^| fin ...