15. 字符与码点
字符字面量 'a' 的类型是 Char:一个 Unicode 标量值,表示就是它的码点。它是
Int 上的 opaque type(§2.7),所以 ==、<、哈希、match 里的字面量模式全都
是 Int 那一份——但它不是 Int,'a' + 1 不成立,两者互转要经 std/char:
char.code(c) 拿码点,char.of(n) 从码点造字符(不是标量值就 None)。
use std/char
use std/str
fn is_digit(c: Char) -> Bool = c >= '0' && c <= '9'
pub fn main() -> Unit !io = {
println(to_string(is_digit('7')))
println(to_string(char.code('a')))
println(to_string(str.len("héllo 🙂")))
println(str.slice("世界你好", 0, 2))
println(from_code_points(['h', 'i']))
}
在 Playground 打开
true
97
7
世界
hi
code_points/from_code_points 在字符串与 List[Char] 间往返(含增补平面的
emoji),str.len 数码点,str.slice 按码点下标切片,str.at 取一个 Char,
str.from_char 把一个 Char 变成字符串。"${c}" 就是同一个单字符字符串:std/char
写了 impl Display[Char],而 Display 是顶层渲染那一层。嵌套那一层的 Show 仍是目标
类型的那一份,所以列表里的 Char 仍打印成码点数字。
按码点下标的函数每次都要从串首数起(单次 O(n),循环里就是 O(n²))。扫描字符串
用 std/cursor:游标是不透明的位置,每步恒定开销;对它做算术是编译错误,
比较先后(==、<)是允许的。
use std/cursor
pub fn main() -> Unit !io = {
let s = "a🎈b"
let c = cursor.next(s, cursor.start(s))
println("${cursor.char(s, c)}")
println(cursor.slice(s, c, cursor.end(s)))
}
在 Playground 打开
127880
🎈b
一步就是一个字符:emoji 的代理对不会被拆开。cursor.char 回的是 Int 不是
Char,因为它到尾要答 -1——一个不是字符的哨兵住不进「每个值都是字符」的类型里
(spec §4.8)。cursor.find(s, sub, from) 返回
Option[Cursor],cursor.skip(s, c, sub) 跳过一段已知出现的字面量。