月度归档:2022年07月

大饼谬论

节选自Paul Graham《Hackers and Painters》

谈到财富总额的时候,财富经常被形容为一个大饼。政治家说:“你无法把饼做的更大。”如果指的是某个家庭银行上的金钱数量或者政府某年的税收,这样收是对的。确实无法把饼做的更大,你分到的越多,别人就分到的越少。

小时候我就对这一点深信不疑:如果富人拿走了所有的钱,那么其他人就变得更穷的。许多成年人至今都是类似看法的信徒。每当有人提到x%的人口占有了y%的财富,他的言下之意往往就包含了这种错误的观点。如果你打算创业,那么不管你是否意识到了,你都是在着手推翻这种大饼谬论。

这里令人混淆的地方就是金钱尤其抽线含义的一面。金钱不是财富,而只是用来转移财富所有权的东西。所以,虽然在某些特定的情况下(比如某个家庭某月的收入),你能用来与他人交换的金钱数量是固定不变的,但是大多数情况下,世界上可供交换的不是一个恒定不变的量。人类历史上的财富一直在不停增长和毁灭(总体上看是净增长)。

假如你有一辆老爷车,你可以不去管它,在家中悠闲度日,也可以自己动手把它修葺一新。这样做的话,你就创造了财富。世界上因为多了一辆修葺一新的车,财富就变得多了一点,对你尤其如此。

通过修理一辆老爷车,你使得自己更富有。与此同时,你也并没有使得任何人变得更贫穷。所以,这里明显是一个不变的大饼。事实上,当这样观察的时候,你会很好奇,为什么有人会觉得大饼的无法增大。

孩子在不知不觉中就懂得了这个道理。如果一个小孩想赠送另一个小孩一件礼物,但是又没有钱,他就会自己动手做。只不过一般情况下小孩子的动手能力不足,相比店里买来的礼物,他们自己做出来的东西从上显达比较粗糙。说实话,我们自己为父母做的松松垮垮的烟灰缸恐怕就很难在市场上卖出去。

字符集与编码方案详细说明

1 文字处理的最朴实场景 – 显示

以文字处理的朴素场景“显示”为例,我们反向推导一下怎么将某个文字在显示器上显示出来。

  • 对显示器来说,它并不知道自己显示的是什么。比如要显示文字“5”,应该给它文字“5”对应的位图数据;
  • 所以,我们首先得从一个地方得到“5”的位图;
  • 可以理解,“5”的位图不是凭空产生的,它必须是预先定义好的一个图片、或一组曲线……其它可显示的文字也应该有各自的绘图数据,这个存储着很多字符的绘图数据的东西,可能在很久以前是一个叫 “汉卡”的硬件、也可能在现在是一个“字体“文件;
  • 如果汉卡或某字体文件中定义了6万个字符,应该怎么找到文字“5”对应的绘图数据呢?
  • 这里出现了一个查询需求。无论是汉卡还是字体文件,它内部必须有一套索引体系,以便调用者查询到某个文字的绘图数据;这套索引实际是一套键值对集合,其中“键”对应要找的字符,“值”对应绘图数据;

到这个地方,整个逻辑就清晰了:

Q:一个信息化系统中,可以显示哪些文字?

A:本系统支持的字符集

Q:在汉卡或字体文件中查询文字的“键”是什么呢?

A:依据本系统中的文字编码方案

Q:各个信息化系统对同一文字,比如大家对“5”的编码方案相同吗?

A:各不相同。但未来的趋势是走向相同

2 字符集

2.1 字符

字符是人类可以理解的最小语言单位,泛指自然语言里所有的文字或符号。在电脑及电信(下文统称信息化)领域,字符是一个信息单位。它大约对应自然语言里的一个单位、符号,或是一个汉字、假名、韩文字,或是一个英文、一个其他西方语言的字母。或是一个音节里的一个音位、类音位……

此外,在信息化领域,还有一个“控制字符”的概念。控制字符并不对应到自然语言中的某个特定符号,而是对应到语言中用来处理文句的概念(类似排版)。如控制打印机的“换行符”、“Tab符”。

所以,我们讲的字符,可分为“可打印字符”和“控制字符”两类。

2.2 字符集

因为字符是人类的主观理解,所以其范围过度宽泛。在电脑及电信领域进行信息化处理时,任何一个子系统需要处理的字符范围都只能是一个有限集。所以标准化组织(国际或国家标准)或国家级行政单位会针对 “需要支持哪些字符”而设定一些规范。这些规定内“字符”的集合就叫字符集。

需要强调的是,字符集规范发布时会为每个字符都指定一个编号,这个编号的本质是一种排序。此编号数值与实际的字符编码数值并不完全一致。虽然大多数信息化系统中ASCII字符的编号正好是编码值。

2.3 编码方案

用什么数值来表示某个字符,是一种规范约定,这种约定即是“编码方案”。一个完整的编码方案,包含两个必须因素:①单个字符的的数据长度;②编码数值与文字的转换方法,这个转换方法可能包含一个编码转换表(代码页)。

2.3.1 早期的代码页方案

由于固定长度数据类型的取值范围是固定的,任何编码方案都是在用有限的整数取值范围去对应不同的字符集。为了支持更多的世界语言, IBM和微软都使用了代码页的概念,在不同语言的系统中使用不同的代码页处理文字。

对于欧洲和西亚语系,可以使用8位整数去对应他们的字符集。一般使用低7位的128个编码空间对应ASCII字符。然后,利用高8位去对应语言内的其它字符。如Windows简体中文系统中使用代码页CP936,繁体中文系统中使用代码页CP950。所以代码页是个字符映射表,早期的Windows系统会带着一个“字符映射表”程序。

各种代码页上的字节流含义是互不相同的。并且,IBM(OEM/IBM PC)的代码页与微软(ANSI)的代码页,即便语言相同,也是互不通用的。一个OEM上的字节流如果想正确的显示在Windows系统中,需要显示指定OME代码页。或显示转换OEM字节流为ANSI字节流,这时需要用到Windows系统的API: OemToChar()。

2.3.2 包含一切的万国码

代码页处理字符的局限显而易见,相同的数据使用不同的代码时,获得了不一样的字符。比如0xB9F1在简体中文CP936里是“国”,在CP950繁体中文里是“弊”。

所以,业界需要一个统一的“字符定义码”去包含世界上所有的字符,这样就可以在同一系统内支持世界语言。

这便是Unicode码,也被称为万国码、单一码、统一码。它是一个包含所有语言、所有字符的排序表,任意一个字符都有一个与之对应的排序号,这个排序号即是字符的Unicode码。Unicode码是一个连续的整数,目前已定义了0x00000-0xfffff区间的用途。①Unicode每隔0xffff区间,也叫1个平面,总共16个平面。

Unicode已被ISO作为国际标准采纳于通用字符集,即 ISO/IEC 10646,Unicode兼容ISO/IEC 10646且完整对应各个版本标准。

3 常见的国家标准字符集

3.1 GB2312

GB/T 2312[注 1],GB/T 2312–80 或 GB/T 2312–1980 是中华人民共和国国家标准简体中文字符集,全称《信息交换用汉字编码字符集·基本集》,通常简称GB(“国标”汉语拼音首字母),又称GB0,由中国国家标准总局于1980年发布,1981年5月1日实施。GB/T 2312编码通行于中国大陆;新加坡等地也采用此编码。中国大陆几乎所有的中文系统和国际化的软件都支持GB/T 2312。

GB/T 2312标准共收录6763个汉字,其中一级汉字3755个,二级汉字3008个;同时收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个字符。

3.2 BIG5

大五码(英语:Big5,又称为五大码)是使用繁体中文(正体中文)社群中最常用的电脑汉字字符集标准,共收录13,060个汉字。

“大五码”(Big5)是由台湾财团法人信息产业策进会为五大中文套装软件所设计的中文共通内码,在1983年12月完成公告,隔年3月,信息产业策进会与台湾13家厂商签定“16位个人电脑套装软件合作开发(BIG-5)项目(五大中文套装软件)”,因为此中文内码是为台湾自行制作开发之“五大中文套装软件”所设计的,所以就称为Big5中文内码。五大中文套装软件虽然并没有如预期的取代国外的套装软件,但随着采用Big5码的国乔中文系统及倚天中文系统先后在台湾市场获得成功,使得Big5码深远地影响繁体中文电脑内码,直至今日。“五大码”的英文名称“Big5”后来被人按英文字序译回中文,以致现在有“五大码”和“大五码”两个中文名称。

3.3 GB13000

GB 13000,中华人民共和国国家标准的国家标准代码之一,全称 GB 13000.1-93《信息技术 通用多八位编码字符集(UCS)第一部分:体系结构与基本多文种平面》。此标准等同采用国际标准化组织 ISO/IEC 10646.1:1993《信息技术 通用多八位编码字符集(UCS)第一部分:体系结构与基本多文种平面》。即“GB 13000.1-93”等同于Unicode 1.1版本。

GB 13000.1-93的字符集包含20,902个汉字,附录是GBK。

2010年1月10日发布的GB 13000-2010取代了GB 13000.1-93,于2010年11月1日实施。是ISO/IEC 10646:2003《信息技术 通用多八位编码字符集(UCS)》等同采用。由中国标准出版社出版,16开1476页。

3.4 GBK

全名为《汉字内码扩展规范(GBK)》1.0版,由中华人民共和国全国信息技术标准化技术委员会1995年12月1日制订,国家技术监督局标准化司和电子工业部科技与质量监督司1995年12月15日联合以《技术标函[1995]229号》文件的形式公布。

GBK的K为“扩展”的汉语拼音(kuòzhǎn)第一个声母。

GBK共收录21886个汉字和图形符号,其中汉字(包括部首和构件)21003个,图形符号883个。

GBK 只为“技术规范指导性文件”,不属于国家标准。国家质量技术监督局于2000年3月17日推出了GB 18030-2000标准,以取代GBK。GB 18030-2000除保留全部GBK编码汉字,在第二字节把能使用范围再度进行扩展,增加了大约一百个汉字及四字节编码空间,但是将GBK作为子集全部保留。

3.5 GB18030

GB 18030,全称《信息技术 中文编码字符集》,是中华人民共和国国家标准所规定的变长多字节字符集。其对GB 2312-1980完全向后兼容,与GBK基本向后兼容,并支持Unicode(GB 13000)的所有码位。GB 18030共收录汉字70,244个。

GB 18030不是一个汉字规范,不定义汉字的写法。中国大陆在此方面的规定由《通用规范汉字表》管理。

3.6 通用规范汉字表

《通用规范汉字表》是《中华人民共和国国家通用语言文字法》的配套规范,是现代记录汉语的通用规范字集,体现着现代通用汉字在字量、字级和字形等方面的规范。2013年6月5日,国务院发出关于公布《通用规范汉字表》的通知,国务院同意教育部、国家语言文字工作委员会组织制定的《通用规范汉字表》,并予公布。《通用规范汉字表》公布后,社会一般应用领域的汉字使用以《通用规范汉字表》为准,原有相关字表停止使用。 该表共收录汉字8105个。

3.7 CJK中日韩统一表意文字

3.7.1 简介

中日韩统一表意文字(英语:CJK Unified Ideographs),也称统一汉字、统汉码(英语:Unihan),目的是要把分别来自中文、日文、韩文、越南文、壮文、琉球文中起源相同、本义相同、形状一样或稍异的表意文字,在ISO 10646及Unicode标准赋予相同编码。

所谓“起源相同、本义相同”、主要是汉字,包括繁体字、简化字、日本汉字(漢字/かんじ)、韩国汉字(漢字/한자)、琉球汉字(漢字/ハンジ)、越南的喃字(𡨸喃/Chữ Nôm)与儒字(𡨸儒/Chữ Nho)、方块壮字(𭨡倱/sawgun)。

3.7.2 历史版本

 Unicode码位版本及数量 
1993.5U+4E00–U+9FFF1.0 20902“〇”(码位U+3007),被当成数字放入了符号和标点区
1999U+3400–U+4DFFExt-A 6582 
2001U+20000–U+2A6FFExt-B 42,711 
2005U+9FA6–U+9FBBExt-B-修订1 22 
2009U+2A700–U+2B734Ext-C 4149 
2010U+2B740–U+2B81FExt-D 224  2012增加1个汉字:U+9FCC
2015U+2B820–U+2CEAF U+9FCD–U+9FD5Ext-E 5776 
2017U+2CEB0–U+2EBEF U+9FD6–U+9FEAExt-F 7488+142018增加5个汉字U+9FEB–U+9FE
  2020U+30000–U+3134A U+9FF0–U+9FFC U+4DB6–U+4DBF U+2A6D7–U+2A6DDExt-G 4939+13+10+6
U+31400–U+33D1F预计放置小篆,U+33E00–U+355FF预计放置甲骨文,相关提案已经提交。按路线图,该平面还会收录金文、简帛文、陶文、鸟虫书等

4 软件开发中的一些问题

4.1 如何读取一个文本文件

读取文本文件的过程是一个将字节流转换为字符串的解码过程。所以必须约定好文本文件中的字符串用什么格式存储。

在Windows系统中,一般使用BOM来指定文本文件的编码方式。即在文本文件的最开头使用一组特定的BOM字节流来指定本文件的文本编码方式。常见的BOM有以下几种。

编码类型BOM(十六进制)BOM (十进制)
UTF-8EF BB BF239 187 191
UTF-16 (BE)FE FF254 255
UTF-16 (LE)FF FE255 254
UTF-32 (BE)00 00 FE FF0 0 254 255
UTF-32 (LE)FF FE 00 00255 254 0 0
UTF-72B 2F 7643 47 118
UTF-1F7 64 4C247 100 76
UTF-EBCDICDD 73 66 73221 115 102 115
SCSU0E FE FF14 254 255
BOCU-1FB EE 28251 238 40
GB-1803084 31 95 33132 49 149 51

在Linux、Apple等系统中,一般不使用BOM,在存储时默认以UTF8存储。读取文本文件时,默认以UTF8方式识别。如果UTF8识别报错,会再尝试以本地语言代码页识别文本。

4.2 源代码中的字符串

在VisualStudio系列软件中,早期的代码文件默认以ANSI方式编码。后来默认以UTF16-LE BOM编码。所以读写源代码文件时,按如下优先级一次识别①识别BOM→②识别为本地代码页。

在跨平台开发中,如果源代码保存为UTF8编码且源文件中包含非ASCII字符时,VisualStudio编译会报无法识别的字符错误。要解决这个问题,可以使用/source-charset:utf-8指定源代码文件的默认编码响应方式。

同样,在GCC系列软件中,默认以UTF8方式编码。早期的GCC可能无法识别BOM头,导致编译报错无法识别的字符。

4.3 内存中的字符串

需要注意的是,VisualStudio开发环境中既区分了源代码的文件编码,又区分了字符串在内存中的编码。对于一个多字节字符串的双引号定义方式,如:const char* text = “大家好”;开发者需要特别关注到此时text中的字符串编码是本地语言的代码页编码。即使文件使用UTF8保存,且指定了/source-charset:utf-8。

也就是说,VisualStudio总是默认以本地语言代码页处理文本。而在GCC环境中,则总是以UTF8方式处理。