掌握 Dart 编程:Unicode 字符的运用

发表时间: 2021-12-04 12:03

Runes 与 grapheme clusters

在 Dart 中,runes 公开了字符串的 Unicode 码位。使用 characters 包 来访问或者操作用户感知的字符,也被称为 Unicode (扩展) grapheme clusters。

Unicode 编码为每一个字母、数字和符号都定义了一个唯一的数值。因为 Dart 中的字符串是一个 UTF-16 的字符序列,所以如果想要表示 32 位的 Unicode 数值则需要一种特殊的语法。【Go 语言中默认使用UTF-8字符编码】

表示 Unicode 字符的常见方式是使用 \uXXXX,其中 XXXX 是一个四位数的 16 进制数字。例如心形字符(♥)的 Unicode 为 \u2665。对于不是四位数的 16 进制数字,需要使用大括号将其括起来。例如大笑的 emoji 表情()的 Unicode 为 \u{1f600}

如果你需要读写单个 Unicode 字符,可以使用 characters 包中定义的 characters getter。它将返回 Characters 对象作为一系列 grapheme clusters 的字符串。下面是使用 characters API 的样例:

import 'package:characters/characters.dart';...var hi = 'Hi ';print(hi);print('The end of the string: ${hi.substring(hi.length - 1)}');print('The last character: ${hi.characters.last}\n');

输出取决于你的环境,大致类似于:

$ dart run bin/main.dartHi The end of the string: ???The last character: 

有关使用 characters 包操作字符串的详细信息,请参阅用于 characters 包的样例 和 API 参考。

机器码

存储单位

数据存储是以“字节”(Byte)为单位,数据传输大多是以“位”(bit,又名“比特”)为单位,一个位就代表一个0或1(即二进制),每8个位(bit,简写为b)组成一个字节(Byte,简写为B),是最小数据存储单位。

1B = 8bit

1KB = 1024B = 8192bit

1MB = 1024KB = 1048576B = 8388608bit

1GB = 1024MB = 1048576KB = 1073741824B = 8589934592bit

1TB = 1024GB = 1048576MB = 1073741824KB = 1099511627776B = 8796093022208bit

字符编码

1.ASCII 码

机器码所有信息最终都是一个二进制值。每一个二进制位(bit)有0和1两种状态,因此八个二进制位就可以组合出256种状态,这被称为一个字节(byte)。也就是说,一个字节一共可以用来表示256种不同的状态,每一个状态对应一个符号,就是256个符号,从00000000到11111111。上个世纪60年代,美国制定了一套字符编码,对英语字符与二进制位之间的关系,做了统一规定。这被称为 ASCII 码,一直沿用至今。

ASCII 码一共规定了128个字符的编码,比如空格SPACE是32(二进制00100000),大写的字母A是65(二进制01000001)。这128个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位,最前面的一位统一规定为0。

2.Unicode

英语用128个符号编码就够了,但是用来表示其他语言,128个符号是不够的。比如,在法语中,字母上方有注音符号,它就无法用 ASCII 码表示。于是,一些欧洲国家就决定,利用字节中闲置的最高位编入新的符号。比如,法语中的é的编码为130(二进制10000010)。这样一来,这些欧洲国家使用的编码体系,可以表示最多256个符号。但是,这里又出现了新的问题。不同的国家有不同的字母,因此,哪怕它们都使用256个符号的编码方式,代表的字母却不一样。

可以想象,如果有一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,那么乱码问题就会消失。这就是 Unicode,就像它的名字都表示的,这是一种所有符号的编码。

3.UTF-8

Unicode 只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。UTF-8 就是在互联网上使用最广的一种 Unicode 的实现方式。其他实现方式还包括 UTF-16(字符用两个字节或四个字节表示)和 UTF-32(字符用四个字节表示),不过在互联网上基本不用。

Runes

The runes (integer Unicode code points) of a String.

Runes 对象是一个 32位 字符对象,用来表示一个字。这样的设计也是考虑兼容 UTF-16 四个字节的情况。

String a = '';print(a.length); //2 // 标识占 2 个 16 位字符print(a.runes.length);//1 // 表示占 1 个 32 位字符print(a.runes); //(128079) // 显示 32 位的 10 进制数值print(a.codeUnitAt(0));//55357   // 第 1 位的 10 进制数值print(a.codeUnits);//[55357, 56399] //显示两位的 10 进制数值


操作 32-bit Unicode 字符

Runes b = Runes('\u{1f596} \u6211'); // 我var c = String.fromCharCodes(b);printc);// 我


或者


String c = '\u{1f596} \u6211'

如果非4个数值,需要用 {…}

返回 16-bit code units 的 codeUnitAt codeUnits

var a = '';print(a.codeUnitAt(0));//55357           // 第 1 位的 10 进制数值print(a.codeUnits);//[55357, 56442]  // 显示 2 位的 10 进制数值

返回 32-bit Unicode 的 runes

var a = '';print(a.runes);//(128079) // 显示 32 位的 10 进制数值

String 操作:

名称	                       说明codeUnitAt	            某个字符的码 10进制fromCharCodes	      Runes 转 String 工厂函数runes	                      返回字对象

下面这个例子展示了runes、十六位的字节单元以及三十二位代码点之间的关系。

var clapping = '\u{1f44f}';print(clapping);    //print(clapping.codeUnits);//[55357, 56399]print(clapping.runes.toList());//[128079]Runes input = new Runes('\u2665  \u{1f605}  \u{1f60e}  \u{1f47b}  \u{1f596}  \u{1f44d}');print(new String.fromCharCodes(input));//♥          

注意:使用数组操作符操作runes时要特别小心。取决于特定语言、字符集、和操作,这个方法特别容易失败。更多信息请参考 How do I reverse a String in Dart? on Stack Overflow.

参考:

https://www.dartlang.org/guides/language/language-tour#runes