本文主要是介绍(delphi11最新学习资料) Object Pascal 学习笔记---第6章第1节(Unicode: 一个适用于整个世界的字母表),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
第6章 字符串
字符串是任何编程语言中都最常用的数据类型之一。在Object Pascal
语言中处理字符串相当简单,速度非常快,而且功能极其强大。尽管字符串的基础知识很容易掌握,而且我在前面的章节中也使用过字符串,但其字符串背后的情况却比乍看起来要复杂一些。文本处理涉及多个密切相关的主题,值得我们深入探讨。要完全理解字符串处理,你需要了解 Unicode 表示法,理解字符串如何映射到字符数组,并学习运行时库中一些最常用的字符串操作,包括把字符串保存文本文件和从文本文件加载字符串。
Object Pascal
有多种字符串操作选项,并提供了不同的数据类型和方法。本章的重点将放在标准字符串数据类型上,但我也会花点时间介绍一些较老的字符串类型,如仍可在 Delphi 编译器中使用的 AnsiString。在此之前,让我Unicode 表示法开始。
6.1 Unicode: 一个适用于整个世界的字母表
Object Pascal
字符串管理的核心是 Unicode
字符集,特别是一种称为 UTF-16
的表示法。在了解字符串的实现技术细节之前,我们值得花几节的篇幅来全面了解 Unicode
标准。
Unicode
背后的理念(这也是它既简单又复杂的原因)是,世界上所有已知字母表中的每一个字符都有相应的描述、图形表示和唯一的数值(称为 Unicode
码点)。
注解:Unicode联盟的参考网站是 http://www.unicode.org,其中有丰富的文档。最终参考是《Unicode标准》书籍,可在 http://www.unicode.org/book/aboutbook.html 找到。
并非所有开发人员都熟悉Unicode,许多人仍然以ASCII和ISO编码等较旧、有限的表示形式来思考字符。通过涵盖这些较旧的标准的简短部分,您将更好地理解Unicode的特殊之处(以及复杂性)。
并非所有开发人员都熟悉 Unicode,许多人仍然认为字符是用 ASCII 等较早的、有限的表示法和 ISO 编码来表示的。通过对这些旧标准的简短介绍,你可以更好地理解 Unicode 的特殊性(和复杂性)。
6.1.1 过去的字符集:从 ASCII 到 ISO 编码
字符表示法始于《美国信息交换标准码》(ASCII),该标准码于 60 年代初制定,是计算机字符的标准编码,包括 26 个小写和大写英文字母、10 个数字、常用标点符号和一些控制字符(至今仍在使用)。
ASCII 使用 7 位编码系统来表示 128 个不同的字符。如图 6.1 所示(摘自 Windows 上运行的 Object Pascal
应用程序),只有 #32(空格)到 #126(tilde)之间的字符才有可视化表示。
ASCII 固然是一个基础字符集(其 128 个基本字符集至今仍是 Unicode 核心的一部分),但它很快就扩展版本所取代,扩展版本使用第八位添加了另外 128 个字符到字符集中。
现在的问题是,世界上有这么多语言,没有简单的方法来确定字符集(有时表示为ASCII-8)中还包括哪些其他字符。长话短说,Windows 采用了一套不同的字符集,称为编码页,其字符集取决于您的本地配置和 Windows 版本。除了 Windows 编码页之外,还有许多其他类似的标准也基于这种分页方法,这些编码页已成为 ISO 国际标准的一部分。
其中最重要的无疑是 ISO 8859 标准,它定义了多个区域字符集。最常用的一套(准确地说,是大多数西方国家使用的一套)是拉丁字符集,称为 ISO 8859-1。
注解:尽管部分相似,但Windows 1252代码页并不完全符合ISO 8859-1字符集。Windows添加了额外的字符,如€符号、额外的引号等,位于128到150的区域。与拉丁字符集的所有其他值不同,这些Windows扩展与Unicode码点不相符。
6.1.2 Unicode码点和字形
如果我真的想要表述地更准确一点,除了码点的概念之外,我还应该再引入一个概念。实际上,有时多个码点可以用于表示一个字形(视觉字符)。这通常不是一个字母,而是一个字母或字母和符号的组合。例如,如果有一个表示拉丁字母a的码点序列(#$0061)后跟一个表示重音符号的码点(#$0300),则应将其显示为一个重音字符。
在Object Pascal
编码术语中,如果您写出以下内容(CodePoints
示例的一部分),信息将有一个单重音字符,如图 6.2 所示。
varStr: String;
beginStr := #$0061 + #$0300;ShowMessage(Str);
图 6.2:一个字形可以是多个码点的结果
在这种情况下,我们有两个字符,代表两个码点,但只有一个字形(或视觉元素)。事实上,在拉丁字母中,您可以使用一个特定的 Unicode 码点来表示给定的字形(带重音的字母 a 的码点是 $00E0),但在其他字母表中,组合Unicode代码点是获得给定图形元素(和正确的输出)的唯一方法。
即使显示的是重音字符,也不会对数值进行自动规范化或转换(只是正确显示),因此字符串的内部结构仍与使用单字符 à 的字符串不同。
注解:用多个代码点呈现字形可能取决于操作系统的具体支持以及正在使用的文本呈现技术,因此您会发现,对于一些字形元素,不是所有操作系统都提供正确的输出。
6.1.3 从码点到字节(UTF)
虽然ASCII对字符到字符的数值表示使用了一种直接且简单的映射,但Unicode使用了一种更复杂的方法。正如我所提到的,Unicode字母表的每个元素都有一个关联的代码点,但到实际表示的映射通常更为复杂。
Unicode 背后令人困惑的一点是,在实际存储、物理字节、内存或文件中,有多种方法来表示同一个码点(或 Unicode 字符对应的数值)。
这个问题源于这样一个事实,即以简单和统一的方式表示所有Unicode代码点的唯一方法是为每个代码点使用四个字节。这说明了需要一个固定长度的表示(每个字符总是需要相同数量的字节),但大多数开发人员会认为这在内存占用和处理时间方面太昂贵了。
注解:在Object Pascal中,通过使用UCS4Char数据类型可以实现4字节的Unicode码点表示。
这就是为什么Unicode标准还定义了其他表示法的原因,这些表示法通常需要的内存更少,但每个符号的字节数不同,具体取决于所需的码点。其思想是对于最常见的元素使用较小的表示,对于不经常遇到的元素使用较长的表示。
Unicode码点的不同物理表示称为Unicode变换格式(UTF)。它们是算法的映射,是 Unicode 标准的一部分,可将每个码点(字符的绝对数字表示)映射为表示给定字符的独特字节序列。请注意,这些映射可以双向使用,在不同表示法之间来回转换。
Unicode标准定义了三种编码格式,具体取决于用于表示字符集的初始部分(前 128 个字符)的比特数:8、16或32。值得注意的是这三种编码格式每个代码点最多需要4个字节的数据。
- UTF-8将字符转换为1到4字节的可变长度编码。UTF-8在HTML和类似协议中很受欢迎,因为当大多数字符(如HTML中的标记)都属于ASCII子集时,UTF-8就显得相当紧凑。
- UTF-16 在许多操作系统(包括 Windows 和 macOS)中都很流行。UTF-16非常方便,因为大多数字符都可以用两个字节来表示,这使得它相当紧凑,处理速度也很快。
- UTF-32在字符处理上非常合理(所有码位长度相同),但它太占用内存,实际使用有限。
有一个常见的误解是,UTF-16可以直接用两个字节映射所有码点,但由于Unicode定义了100,000多个码点,您很容易发现它们无法容纳在64K个元素中。然而,确实存在开发人员仅使用Unicode的子集的情况,以使其适应固定长度的2字节每字符表示形式。在早期,Unicode的这个子集称为UCS-2,现在您经常看到它被引用为基本多语言平面(BMP)。但是,这只是Unicode的一个子集,是众多平面的一个。
注意:与多字节表示形式(UTF-16 和 UTF-32)相关的一个问题是确定哪个字节在前。根据该标准,所有形式都是允许的,因此您可以拥有 UTF-16 BE(大端)或 LE(小端),UTF-32 也是如此。big-endian 字节序列化是最高有效字节在前,little-endian 字节序列化是最低有效字节在前。字节序列化通常与带有称为字节顺序标记 (BOM) 的标头的 UTF 表示形式一起标记在文件中。
6.1.4 字节顺序标记
在以 Unicode 字符存储的文本文件中,有一种方法可以显示码点所使用的 UTF 格式。这些信息存储在文件开头的标头或标记中,称为字节序标记(BOM)。这是一个签名,表示所使用的 Unicode 格式和字节序形式(小端或大端—LE 或 BE)。下表提供了各种 BOM 的汇总,其长度可以是 2、3 或 4 字节:
- 00 00 FE FF UTF-32,big-endian
- FF FE 00 00 UTF-32,little-endian
- FE FF UTF-16,big-endian
- FF FE UTF-16,little-endian
- EF BB BF UTF-8
在本章后面,我们将看到 Object Pascal 如何管理流中 BOM。BOM 出现在文件的开头,实际的 Unicode 数据紧随其后。因此,一个内容为 AB 的 UTF-8 文件包含五个十六进制值(3 个 值是BOM,2 个值是字母):
EF BB BF 41 42
如果文本文件没有这些签名中的任何一个,通常将其视为ASCII文本文件,但它也可能包含任何编码的文本。
注解:另一方面,当您从网络请求或通过其他互联网协议接收数据时,可能会有一个特定的标头(协议的一部分)来指示编码,而不是依赖于 BOM。
6.1.5 观察Unicode
如何创建一个 Unicode 字符表,就像我之前展示的 ASCII 字符表一样?首先,我们可以在基本多语言平面(BMP)中显示码点,但不包括所谓的代理对。
注解: 并非所有数值都是真正的 UTF-16 码点,因为有些字符(称为代用字符)的数值是无效的,它们被用来组成码点对并代表 65535 以上的码点。乐谱中用于 F(或低音)谱号的符号𝄢就是一个很好的代理对的例子。它的 3 字节码点 1D122 在 UTF-16 中用两个值(D834 和 DD22)的 4 字节来表示。
要显示 BMP 的所有元素,需要一个 256 * 256 的网格,这很难在屏幕上显示。因此,ShowUnicode 示例中有一个包含两个页面的选项卡: 第一个标签页是包含 256 个区块的主选择器,而第二个页面则显示包含实际 Unicode 元素的网格,每次显示一个部分。与书中的其他程序相比,该程序的用户界面更简洁一些,如果你只对程序的输出结果感兴趣,而对程序的内部结构不感兴趣,你可以简单地浏览一下程序代码。
程序启动时,会在 TabControl 第一页的 ListView 控件中填入 256 个条目,每个条目表示一组 256 个字符中的第一个和最后一个字符。以下是窗体的 OnCreate 事件处理器的实际代码,以及用于显示每个元素的简单函数,产生了图 6.3 的输出结果:
// 辅助函数
function GetCharDescr(NChar: Integer): string;
beginif Char(NChar).IsControl thenResult := 'Char #' + IntToStr(NChar) + ' [ ]'elseResult := 'Char #' + IntToStr(NChar) + ' [' + Char(NChar) + ']';
end;procedure TForm2.FormCreate(Sender: TObject);
varI: Integer;ListItem: TListViewItem;
beginfor I := 0 to 255 do // 256个页面 * 每个页面256个字符beginListItem := ListView1.Items.Add;ListItem.Tag := I;if (I < 216) or (I > 223) thenListItem.Text := GetCharDescr(I * 256) + '/' + GetCharDescr(I * 256 + 255)elseListItem.Text := 'Surrogate Code Points';end;
end;
图6.3:ShowUnicode示例的第一页有一个长列表,显示Unicode字符的各个部分
请注意代码是如何在 ListView 项目的 Tag 属性中保存 "页面 "编号的,这些信息随后将用于填充页面。当用户选择其中一个条目时,应用程序将跳转到 Tab控件的第二页,并用该部分的 256 个字符填充其字符串网格:
procedure TForm2.ListView1ItemClick(const Sender: TObject; const AItem: TListViewItem);
varI, NStart: Integer;
beginNStart := AItem.Tag * 256;for I := 0 to 255 dobeginStringGrid1.Cells[I mod 16, I div 16] :=IfThen(not Char(I + NStart).IsControl, Char(I + NStart), '');end;TabControl1.ActiveTab := TabItem2;
end;
上述代码中使用的 IfThen 函数是一个双向测试:如果第一个参数传递的条件为真,函数返回第二个参数的值;如果不为真,则返回第三个参数的值。对第一个参数进行的测试使用了 Char 类型助手的 IsControl 方法来过滤不可打印的控制字符。
注解:IfThen 函数的操作与大多数基于 C 语法的编程语言中的 ? :运算符类似。它有一个用于字符串的版本和一个用于整数的单独版本。对于字符串版本,必须包含 System.StrUtils 单元,而对于整数版本的 IfThen,则必须包含 System.SysUtils 单元。
应用程序生成的 Unicode 字符网格如图 6.4 所示。请注意,输出结果因所选字体和具体操作系统显示特定 Unicode 字符的能力而异。
图 6.4: ShowUnicode 示例的第二页显示了一些的 Unicode 字符
这篇关于(delphi11最新学习资料) Object Pascal 学习笔记---第6章第1节(Unicode: 一个适用于整个世界的字母表)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!