〇、前言
最近在某群聊天时发现有群友连字符串的基本特点都不了解,各种文本编码也混为一谈,所以特开此贴科普字符串与文本编码的有关知识。
一、字符集简史
1. 美国标准
ASCII(AmericanStandard Code for Information Interchange,美国信息交换标准代码)于1963年发布第一个标准,于1967年进行了一次主要修正,此后它成为了世界上几乎所有主流文本编码方式的基础。由于时代技术限制,ASCII最初制定时为7位编码 ,包含26个拉丁字母(含大小写,不含变音符号)、10个数字、英式标点和其他一些字符以及控制字符,共有127个字符。
2. 扩展ASCII
后来8位=1字节的标准得以巩固,因此如果用1个8位字节表示ASCII就会余出128个空位。前面也提到ASCII并不能表示变音符号,它甚至也没有英镑符号,因此多出来的128个空位开始被用来放置扩展字符以适应其他国家的需求,由此引申出来不同的编码方案,它们被统称为EASCII(Extended ASCII,延伸美国标准信息交换码)。
a) IBM扩展字符集
这个字符集包含了一部分(不是所有) 含变音符号的拉丁字母,还包含了一些图线字符,甚至还包含了几个表情符号。在早期的IBM PC上它被无数的程序使用,并被烧制在各种视频板和打印机的ROM中。
b) ISO-8859-1(ANSI)
到了Windows发展时期,IBM的EASCII已不再适合新环境,一是它没有包含所有字母的变音符号,二是Windows已有GDI作为图形输出系统,因此不再需要图线字符。考虑到其影响之深远,Windows并未完全将其放弃,而是降级到次要地位。Windows自有的字符集称为ANSI字符集(后来ANSI变成了Windows字符集的统称),它基于ANSI/ISO草案,这个草案最终成为ISO-8859-1(1987年发布)“美国国家信息处理标准——8位单字节编码图形字符集——第1部分:拉丁字母第1号”,简称拉丁语-1。
PS.
a) ISO-8859还有其他的部分,如ISO-8859-5为西里尔字母(俄语)ISO-8859-6为阿拉伯语。
b) ANSI字符集后来扩展出了一个字符集,称为Windows-1252,添加了欧元符号等。
3. 持续扩展的ASCII与代码页
MS-DOS3.3引入代码页的概念。代码页是IBM称呼计算机的BIOS所支持的字符集编码,后来这一概念扩展,代码页变成了字符编码方案的别称(如在Windows上简中GB2312为936页,繁中BIG5为950页,GB18030为54936页)。
上面提到的几个字符集都只有拉丁字母,俄语和阿拉伯语等必须制定另外的标准,因此代码页的数量持续增加,甚至同一语种在不同平台上的编码还不同。而且这里还忽略了一个重要问题,到目前为止我们一直在谈字母语言,但在东半球还有使用汉字的中日韩,这些字符编码的解决方案是使用两个字节(DBCS,双字节字符集)——更准确的说,前128个码位与ASCII兼容,编码超出256的才使用两个字节,这也就意味着它是一种变长编码(处理时善用IsDBCSLeadByte )。
4. Unicode
a) 简介
因为代码页的局限性,用户在不同语言环境间切换时,如果代码页未正确指定则会造成乱码。现有的问题是世界上的语言符号太多以至于无法只用1个字节来表示而人们又需要一种统一的文本编码标准。人们自然而然地想到使用更长的字节来表示字符——并且是表示世界上的所有字符——于是,Unicode应运而生。最初这个标准由商业Unicode组织和ISO-10646工作小组同时制订,不久后两者合并工作成果,现在Unicode的发展由非营利机构统一码联盟负责。截止到2022年9月,Unicode已收录14万+字符。
b) 编码实现
Unicode编码有多种不同的实现方式,最最主流的有UTF-8、UTF-16LE、UTF-16BE,此外还有UTF-7、UTF-32等。
UTF-8:
在网页中广泛应用,优点是表示英文只需要一个字节,这种情况下与ASCII基本互通,但是表示汉字却最少需要三个字节。
UTF-16LE:
Windows使用的方案,最少用16位值(与短整数型等宽,两个字节)表示一个字符,有的字符则要再扩展一个16位值共同组成一个32位值表示一个字符,所以它本质上也是一种变长编码。
UTF-16BE:
与UTF-16LE的字节序相反。
二、字符串的内存模型
1. 简介:
字符串通常以空字符结尾(具体几个字节取决于编码方式 ,如ANSI与UTF-8都以一个0字节结尾,而UTF-16以两个0字节结尾),称作空终止字符串或以零结尾的字符串,结尾的空字节称作结尾NULL,各种函数经常使用结尾NULL来判定字符串结束与否。
2. 使用
易语言 字节集表示Unicode文本应当注意什么?
首先是结尾NULL的问题,一般来说结尾NULL都要求存在,否则使用取字节集长度() 获取字节流长度。
其次拼接文本不能简单字节集相加,应当去除结尾NULL,否则文本将在中间被截断。(参考辅助API:lstrcatW)
三、文本编码的互转
1. MultiByteToWideChar :将ANSI、UTF-7、UTF-8转换为UTF-16LE。
WideCharToMultiByte :将UTF-16LE转换为ANSI、UTF-7、UTF-8。
注意:这两个函数的缓冲区计数问题臭名昭著,使用时应该特别注意。
2. LCMapStringEx :UTF-16LE与UTF-16BE互转,还可以做繁简转换。
四、通用文本映射简介
大多数涉及字符串的API都有A与W两个版本,为了使源码维护方便,微软引入了TCHAR和一系列相关的宏,当UNICODE宏定义时调用转向Unicode版本,否则为ANSI版本。微软文档中可能见到“以TCHAR计”的描述,意思即为ANSI字符串计CHAR(1字节),Unicode字符串计WCHAR(2字节)。