- 主题:用c++做了一个项目生不如死
C++应该像python那样把字符串和二进制字节串分开,各自定义自己的类。
【 在 hgoldfish 的大作中提到: 】
: std::string 最大的问题是它不是 unicode 的。这导致他在现实中几乎无用。对于我们中国人尤其如此。在表情符到处飞的今天,继续用 std::string 就是犯罪啊!
: 拿它来处理网络流很好用。但它真的不是字符串。
: 话说,有没有谁做个真正好用的字符串第三方库啊。其它的功能不用搞,就做好字符串就行了。
: ...................
--
FROM 111.200.40.*
这些本来该C++自己完成的,结果自己失职,却让第三方的qt去完成。
【 在 siyuetian 的大作中提到: 】
: std::string把他看成个字符串类,那他真的不好用,但问题是这玩意设计上只是个容器。
: 对于你的需求,如果用到qt的话qstring挺好用,没用qt的话,icu::UnicodeString试试,icu库挺通用的
--
FROM 111.200.40.*
不知道u16string是不是utf-16.
utf-16原本对应unicode基本平面(65536个字符,其中汉字为gbk+cjk-extA,约有2.8万个),后来unicode字符集不断扩充,超出了基本平面的容纳范围。超出基本平面的汉字utf-16编码就不是双字节编码了,这时utf-16就名不符实了,它成了和utf-8一样的变长编码。当然我们一般人用的字符都在基本平面内,几乎不太可能用到超出两个字节的utf-16字符。所以实践中,还是有不少人直接把utf-16的字符串当成定长变码来对待。但理论上,utf-16已经不符合它原来的定义了(于是出现了utf-32,用来代替utf-16,这个是真正的定长编码方式,不过与utf-8比起来,空间效率更低)。
基本平面内的汉字,三字节的utf-8编码和两字节的utf-16编码,对于今天的计算机存诸技术和网络带宽而言,不是一个值得纠结的点,如果加上zip等无损压缩手段,两字节相对于三字节编码的空间优势更是可以忽略不计(比如在epub、odt文件中)。对于变长的utf-8编码,直接取下标定位字符技术上也是可以做到的。
定长编码的唯一优势是,取下标操作的时间复杂度是常数,而变长编码取下标操作的时间复杂度是线性的。以今天的计算机速度,定长编码(基本平面内的utf-16, 所有平面内的utf-32)的这点优势不值一提。
在编程语言中向程序员暴露每个字符的编码长度(u16、u32)就是误入歧途,程序员只须关心他存的是哪些字符和编码方案即可(比如 a = string('国家',coding='utf-32')),程序设计语言应该封装具体的码长信息,所有关于字符串的操作都应该基于字符而非关注于其底层编码,非字符/二进制数据应该存放在bytes中,这时应该向程序员暴露字节长度。
utf-8的优势是很明显的。首先它是自同步的,无需额外加入大端小端信息,允许读取程序从任何位置开始并立即检测到字符边界,这是非常重要的一个优点。其次它采用变长编码方式,对最常用的英文字母采取1个字节编码,节省了大量空间。由于长度可以不断扩充,因此本身保持开放性,可以和unicode一同演进。采用变长编码方式还使程序不得依赖/限定文字的编码长度,在更抽象的水平上操作文字,而不是依赖于strcpy、strcat、strstr、strlen、strdup等底层函数。变长编码方式也不影响grep等RE工具的使用。
定长编码不值得留恋。
【 在 hgoldfish 的大作中提到: 】
: 如果多用中文的话,应该是 u16string 最省空间。基本上一个汉字对应两个字节。
: 而且 utf-8 的话,一个汉字基本上对应三个字节。
: python 和 Qt 都会做我刚才说的优化,自动根据 unicode 编码范围,选择适合的存储类型来使用。一来可以节省空间,二来提升处理效率。比如 u8string 里面,想要迭代处理一个个 unicode 字符,一会是一个字节,一会儿是三个字节。效率很差。
: unicode_string[i]
: 就这个简单的取下标,都是大麻烦。
--
修改:seablue FROM 111.200.40.*
FROM 111.200.40.*
没办法,C++不愿大刀阔斧动它的标准库。相比之下,python在十几年前的version 3.0中以破坏语言和库的兼容性为代价,以unicode为基础重新设计语言,并且把string和binary分开,是十分明智的,也成就了今天python在云计算、AI领域的地位。早点改,阵痛还比较小。越往后,代码量和用户量越大,越尾大不掉。C++就没有这样的魄力。它一直带着沉重的历史资产包袱。
要我说,C++的编程就在kde环境下进行,不考虑非kde环境。只有这样才能抵消C++难用带来的痛苦。
用户没有运行环境就让他装虚拟机。
【 在 hgoldfish 的大作中提到: 】
: 问题就是想要补全它很难啊。
: QtCore 好用。但是为了个 QtCore 依赖 Qt 一大堆东西又很麻烦。
: 有空研究一下好像有个改出了个类 QtCore 的模块。
: ...................
--
修改:seablue FROM 111.200.40.*
FROM 111.200.40.*
python runtime中的编码应该是由locale/code page决定的。文件中的编码由文件的开头声明决定,默认是utf-8. Python will enable UTF-8 mode(运行时环境) by default from Python 3.15. 这个影响最大的是windows平台,因为mac和linux上,locale通常默认是utf-8.
peps.python.org/pep-3120/
peps.python.org/pep-0686/
在内部,python解释器应该是把各种编码转成某种unicode定长编码(比如ascii、utf-16、utf-32)来分别处理:
Python 3.3 and later (PEP 393):
peps.python.org/pep-0393/
The internal representation can be one of three forms:
- PyASCIIObject:
Used for strings containing only ASCII characters (code points up to 127). These are stored using 1 byte per character.
- PyCompactUnicodeObject:
Used for strings containing characters from the Basic Multilingual Plane (BMP), but not beyond. These are stored using 2 bytes per character (similar to UTF-16).
- PyUnicodeObject:
Used for strings containing characters outside the BMP (requiring code points up to 1,114,111). These are stored using 4 bytes per character (similar to UTF-32).
This flexible representation means that Python dynamically adjusts the internal storage width to minimize memory consumption, unlike older versions (Python 2 and early Python 3) that used a fixed-width UCS-2 or UCS-4 representation determined at compile time.
For example, a string containing only "hello" would be stored using 1-byte characters, while a string containing "hello?" would use a 4-byte representation because of the emoji character. This allows for efficient memory usage while maintaining the ability to represent the full range of Unicode characters.
==========
In Python 3.3 which implements PEP 393. The new representation will pick one or several of ascii, latin-1, utf-8, utf-16, utf-32, generally trying to get a compact representation.
==========
python对字符串的这种处理方式——程序员可以只使用utf-8编码,而内部将字符串拆成ascii、utf-16、utf-32定长编码——既对程序员屏蔽了细节,又使用定长编码来节约存诸空间、加快下标取值速度和方便计算字符串长度,值得C++借鉴。
C++的步子太小了,还把宽窄字符串这种内存细节暴露给程序员,这不是一种现代化的处理思路。
【 在 easior 的大作中提到: 】
: Python 的字符串底层应该是建立在 C 的多字节流之上的吧
: C 的这一套处理方案依赖于本地策略集,也容易出现乱码
: Python 3 预设了所有编码都是 UTF-8,不知道虚拟机如何处理运行环境的?
: ...................
--
修改:seablue FROM 111.200.40.*
FROM 111.200.40.*
我前面的贴子说了:原来的utf-16现在不够用了,于是扩成了变长变码。
那既然用了变长编码,为啥不用utf-8?毕竟utf-8自带同步信息,而且在ascii内空间效率更高,也不用再区分宽窄字符串。
java/js的unicode string是u16,这是历史遗留问题。所以现在很尴尬。
python和apple的swift是少有的默认以utf-8处理字符的语言。swift是因为比较新,历史包袱小,python是因为破坏了兼容性,用了很长时间才从python2过渡到python3,现在已不再区分宽窄字符。大多数语言为了保持兼容性,没有将默认的字符串处理方式改成utf-8,只是把utf-16扩充成变长编码方式,不再依赖它的定长编码算法。比如JavaScript从 ES2015 开始添加了字符串方法和正则表达式标志,允许从与编码无关的角度处理字符串。——这就不依赖于定长编码方式了。
【 在 kirbyzhou 的大作中提到: 】
: 你就把u16string拿来用有啥不行的吗?
: java的string就是u16啊
--
修改:seablue FROM 111.200.40.*
FROM 111.200.40.*
表情符号要么没法存,要么超出了两字节。所以utf-16是一个残次品。
【 在 hgoldfish 的大作中提到: 】
: 也不算尴尬。
: Java 的 u16string 在互联网时代存储汉字更高效。
--
修改:seablue FROM 111.200.40.*
FROM 111.200.40.*
这有什么尴尬的?编码空间不够了,自然就得上长编码。
像gbk那样,自己两字节定长是爽了,但是不顾别国。所以出现了各种utf. 就是为了把大家的文字都放一个编码空间,同时显示。
utf-16也是同样的问题,它在两字节内(UCS-2)已经满员了,为了处理表情符号,只能扩充。要怪就怪unicode/utf-16目光短浅(其实也是为了追求那点可怜的空间效率:IEEE曾引入了更大的 31 位空间和一种编码(UCS-4),每个字符需要 4 个字节。这遭到了Unicode 联盟的抵制,一方面因为每个字符 4 个字节会浪费大量内存和磁盘空间,另一方面一些制造商已经在 2 个字节/字符技术上投入了大量资金)。当年认为只要2字节(65536个编码空间)就能编码地球上的所有文字,没想到今天的unicode已经发展到了16万个字符(其中10万个是汉字)。unicode可以很方便地增补扩充,因为人家只是负责出版文本,增补时多印几页纸就行了。可是与之对应的utf-16就没那么容易扩充,有不少程序已经固定了(java),有了历史代码,在这种情况下,如果utf-16的编码方式变了,这些程序就很尴尬。为了避免utf-16的缺点,出现了utf-32,它就是为了取代utf-16而来的,可是让这些程序用utf-32,有些人又舍不得那点编码空间。
而且utf-16对英文也是2字节编码,为了节省空间,c++中处理英文用的是ascii(窄字符)。这不是跟python内部一样的拆分思路吗?只不过在c++中交给了程序员自己处理。
在C++/java中,
英文字母:ascii (窄字符:单字节)
基本平面内的2.8万个中文:u16 (宽字符:双字节)
表情符号:u32 (宽字符:四字节)
这跟python内部的方法是一样的。
=====
from wiki: Windows API以及许多编程环境(例如Java和Qt )都使用 UTF-16。UTF-16 的
可变长度特性,加上大多数字符的长度并非可变(因此很少测试可变长度),导致了许多软
件(包括 Windows 本身)出现错误。
由于最常用的字符都包含在 BMP 中,代理对的处理通常未经彻底测试。这会导致持续存在的 bug 和潜在的安全漏洞,即使是在热门且备受好评的应用软件中也是如此(例如CVE - 2008-2938、CVE -2012-2135)。
一个“字符”可以使用任意数量的 Unicode 码位。[ 21 ]例如,一个表情符号标志字符占用 8 个字节,因为它“由一对 Unicode 标量值构成” [ 22 ](这些值位于 BMP 之外,每个值需要 4 个字节)。UTF-16 无法辅助“计算字符数”或“测量字符串的宽度”。
对于东亚语言来说,UTF-16 通常被认为比UTF-8更节省空间,因为它用两个字节来表示 UTF-8 中占用三个字节的字符。由于实际文本包含许多空格、数字、标点符号、标记(例如网页标记)和控制字符,而这些字符在 UTF-8 中只占用一个字节,因此 UTF-16 的这种说法仅适用于人为构建的密集文本块。[需要引用]对于天城文和孟加拉语来说,情况可能更糟,因为它们包含多字母单词,所有字母在 UTF-8 中占用三个字节,而在 UTF-16 中仅占用两个字节。
此外,中文 Unicode 编码标准GB 18030对所有语言(而不仅仅是中文)始终生成与 UTF-16 大小相同或更小的文件(它通过牺牲自同步来实现这一点)。
自Windows 10 版本 1903(或内部版本17035)起,API 中已经可以使用 UTF-8,[ 28 ]尽管大多数软件(例如Windows 文件资源管理器)仍然使用 UTF-16 API。微软表示,“UTF-16 [..] 是 Windows 对针对多平台的代码施加的独特负担” [ 29 ]文件和网络数据往往是 UTF-16、UTF-8 和传统字节编码的混合。
短信实际上使用的是 UTF-16。文档中指定的是 UCS-2,但表情符号必须使用 UTF-16 才能正常工作。[ 30 ]
【 在 hgoldfish 的大作中提到: 】
: 但 utf-8 也很尴尬啊。印象中表情符需要 6 个 utf-8 字节才能存得下?
--
修改:seablue FROM 111.200.40.*
FROM 111.200.40.*