仓颉标准库 String 类型 Skill
- 概述
String 是仓颉核心包 std.core 中的 struct 类型,无需导入 即可直接使用。
-
不可变值类型(赋值产生副本)
-
仅支持 UTF-8 编码,最大长度为 UInt32.Max (约 4 GB)
-
实现接口:Collection<Byte> 、Comparable<String> 、Hashable 、ToString
注意:String 实现了 Collection<Byte> ,for (b in s) 迭代的是 UTF-8 字节(Byte / UInt8 ),而非字符。若要按字符迭代,请使用 for (r in s.runes()) 。
- 构造
// 空字符串 let s1 = "" let s2 = String() let s3 = String.empty // 静态常量
// 字符串字面量 let s4 = "Hello, 仓颉!"
// 多行字符串(三引号) let s5 = """ 第一行 第二行 """
// 从 Rune 数组构造 let runes: Array<Rune> = [r'H', r'i'] let s6 = String(runes) // "Hi"
// 从 Rune 集合构造 let s7 = String(someRuneCollection)
// 从 UTF-8 字节数组构造 let bytes: Array<UInt8> = [72, 101, 108, 108, 111] let s8 = String.fromUtf8(bytes) // "Hello"(会校验 UTF-8 合法性)
- 属性
属性 类型 说明
size
Int64
UTF-8 编码字节长度(不是字符数)
let s = "Hello" println(s.size) // 5
let s2 = "你好" println(s2.size) // 6(每个汉字 3 字节 UTF-8)
- 静态方法
4.1 join — 拼接字符串数组
static func join(strArr: Array<String>, delimiter!: String = String.empty): String
let arr = ["I", "like", "Cangjie"] let s = String.join(arr, delimiter: " ") println(s) // "I like Cangjie"
let csv = String.join(["a", "b", "c"], delimiter: ",") println(csv) // "a,b,c"
4.2 fromUtf8 — 从 UTF-8 字节数组构造
static func fromUtf8(utf8Data: Array<UInt8>): String
- 校验字节数组是否为合法 UTF-8,非法则抛出 IllegalArgumentException
let bytes: Array<UInt8> = [72, 101, 108, 108, 111] let s = String.fromUtf8(bytes) // "Hello"
4.3 fromUtf8Unchecked — 不校验构造(unsafe)
static func fromUtf8Unchecked(utf8Data: Array<UInt8>): String
- 不校验 UTF-8 合法性,性能更好但使用不当会导致未定义行为
4.4 checkUtf8Encoding — 校验 UTF-8 合法性
static func checkUtf8Encoding(data: Array<UInt8>): Bool
let valid = String.checkUtf8Encoding([72, 101]) // true let invalid = String.checkUtf8Encoding([0xFF, 0xFE]) // false
- 搜索与检查
5.1 contains — 包含子串
func contains(str: String): Bool
"Hello World".contains("World") // true "Hello World".contains("world") // false(大小写敏感)
5.2 startsWith / endsWith — 前缀/后缀检查
func startsWith(prefix: String): Bool func endsWith(suffix: String): Bool
"hello.cj".startsWith("hello") // true "hello.cj".endsWith(".cj") // true
5.3 indexOf — 查找首次出现位置
func indexOf(b: Byte): Option<Int64> func indexOf(b: Byte, fromIndex: Int64): Option<Int64> func indexOf(str: String): Option<Int64> func indexOf(str: String, fromIndex: Int64): Option<Int64>
let s = "Hello World" s.indexOf("World") // Some(6) s.indexOf("xyz") // None s.indexOf("l") // Some(2) s.indexOf("l", 3) // Some(3) — 从索引 3 开始搜索
5.4 lastIndexOf — 查找最后出现位置
func lastIndexOf(b: Byte): Option<Int64> func lastIndexOf(b: Byte, fromIndex: Int64): Option<Int64> func lastIndexOf(str: String): Option<Int64> func lastIndexOf(str: String, fromIndex: Int64): Option<Int64>
"abcabc".lastIndexOf("abc") // Some(3)
5.5 count — 统计子串出现次数
func count(str: String): Int64
"abababab".count("ab") // 4
5.6 isEmpty / isAscii / isAsciiBlank
func isEmpty(): Bool // 是否为空串 func isAscii(): Bool // 是否全为 ASCII 字符 func isAsciiBlank(): Bool // 是否为空串或仅含 ASCII 空白字符
- 替换与删除
6.1 replace — 替换所有匹配子串
func replace(old: String, new: String): String
"aabbcc".replace("bb", "XX") // "aaXXcc" "aaa".replace("a", "bb") // "bbbbbb"
6.2 removePrefix / removeSuffix — 删除前缀/后缀
func removePrefix(prefix: String): String func removeSuffix(suffix: String): String
- 如果字符串不以指定前缀/后缀开头/结尾,返回原字符串
"HelloWorld".removePrefix("Hello") // "World" "HelloWorld".removeSuffix("World") // "Hello" "HelloWorld".removePrefix("xyz") // "HelloWorld"
- 分割
7.1 split — 按分隔符分割
func split(str: String, removeEmpty!: Bool = false): Array<String> func split(str: String, maxSplits: Int64, removeEmpty!: Bool = false): Array<String>
"a,b,,c".split(",") // ["a", "b", "", "c"] "a,b,,c".split(",", removeEmpty: true) // ["a", "b", "c"] "a,b,c,d".split(",", 2) // ["a", "b", "c,d"] — 最多分割 2 次
7.2 lazySplit — 惰性分割(返回迭代器)
func lazySplit(str: String, removeEmpty!: Bool = false): Iterator<String> func lazySplit(str: String, maxSplits: Int64, removeEmpty!: Bool = false): Iterator<String>
- 与 split 功能相同,但返回 Iterator<String> ,适合处理大字符串时避免一次性分配
7.3 lines — 按行分割
func lines(): Iterator<String>
let text = "line1\nline2\nline3" for (line in text.lines()) { println(line) } // 输出: // line1 // line2 // line3
- 裁剪(Trim)
8.1 ASCII 空白裁剪
func trimAscii(): String // 两端裁剪 ASCII 空白 func trimAsciiStart(): String // 裁剪前导 ASCII 空白 func trimAsciiEnd(): String // 裁剪尾部 ASCII 空白
" hello ".trimAscii() // "hello" " hello ".trimAsciiStart() // "hello " " hello ".trimAsciiEnd() // " hello"
8.2 自定义裁剪
// 按 Rune 数组裁剪(移除集合中的任意字符) func trimStart(chars: Array<Rune>): String func trimEnd(chars: Array<Rune>): String
// 按字符串裁剪(移除前缀/后缀子串的重复出现) func trimStart(str: String): String func trimEnd(str: String): String
// 按谓词裁剪(移除满足条件的字符) func trimStart(predicate: (Rune) -> Bool): String func trimEnd(predicate: (Rune) -> Bool): String
"xxxHelloxxx".trimStart([r'x']) // "Helloxxx" "xxxHelloxxx".trimEnd([r'x']) // "xxxHello" "123abc456".trimStart { r => r >= r'0' && r <= r'9' } // "abc456"
- 填充(Pad)
func padStart(totalWidth: Int64, padding!: String = " "): String func padEnd(totalWidth: Int64, padding!: String = " "): String
-
totalWidth 为目标字节宽度
-
如果原字符串长度已 ≥ totalWidth ,返回原字符串
"42".padStart(6) // " 42" "42".padStart(6, padding: "0") // "000042" "42".padEnd(6) // "42 " "42".padEnd(6, padding: ".") // "42...."
- 大小写转换
func toAsciiLower(): String // 转小写(仅 ASCII 字母) func toAsciiUpper(): String // 转大写(仅 ASCII 字母) func toAsciiTitle(): String // 首字母大写(仅 ASCII 字母)
"Hello World".toAsciiLower() // "hello world" "Hello World".toAsciiUpper() // "HELLO WORLD" "hello world".toAsciiTitle() // "Hello World"
- 比较
func compare(other: String): Ordering // 字典序比较,返回 Ordering.LT/EQ/GT func equalsIgnoreAsciiCase(other: String): Bool // 忽略 ASCII 大小写比较
"abc".compare("abd") // Ordering.LT "Hello".equalsIgnoreAsciiCase("hello") // true
运算符比较
"abc" == "abc" // true "abc" != "def" // true "abc" < "abd" // true(字典序) "abc" <= "abc" // true "abd" > "abc" // true "abd" >= "abc" // true
- 拼接与重复
12.1 + 运算符 — 字符串拼接
let s = "Hello" + ", " + "World!" // "Hello, World!"
12.2 * 运算符 — 重复
let s = "ab" * 3 // "ababab" let line = "-" * 20 // "--------------------"
12.3 字符串插值 "${expr}"
let name = "Cangjie" let age = 3 let s = "Name: ${name}, Age: ${age}" // "Name: Cangjie, Age: 3"
12.4 String.join — 拼接数组
let parts = ["2024", "06", "15"] let date = String.join(parts, delimiter: "-") // "2024-06-15"
12.5 StringBuilder — 高效拼接
大量拼接时建议使用 StringBuilder :
let sb = StringBuilder() sb.append("Hello") sb.append(", ") sb.append("World!") let result = sb.toString() // "Hello, World!"
- 转换
13.1 转为字节数组
func toArray(): Array<Byte> // 返回 UTF-8 字节数组(拷贝) func rawData(): Array<Byte> // 返回内部原始数据引用(unsafe)
let bytes = "Hi".toArray() // [72, 105]
13.2 转为 Rune 数组
func toRuneArray(): Array<Rune>
let runes = "Hi你".toRuneArray() // [r'H', r'i', r'你']
13.3 迭代
func iterator(): Iterator<Byte> // 按字节迭代 func runes(): Iterator<Rune> // 按字符(Rune)迭代
// 按字符迭代(推荐) for (r in "Hello 仓颉".runes()) { print(r) }
// 按字节迭代 for (b in "Hello") { print(b) // 输出 UTF-8 字节值 }
13.4 toString
func toString(): String // 返回自身
13.5 hashCode
func hashCode(): Int64 // 返回哈希值,可用于 HashMap 等
- 下标访问与切片
14.1 按字节索引
let s = "Hello" let b: Byte = s[0] // 72 (H 的 UTF-8 编码) let opt = s.get(10) // None(安全访问,不抛异常)
注意:s[i] 返回的是 Byte (UTF-8 编码字节),对于多字节字符(如中文),单个索引不能获取完整字符。
14.2 切片
let s = "Hello World" let sub = s[0..5] // "Hello" let sub2 = s[6..11] // "World"
注意:切片范围基于字节索引,确保不要在多字节字符中间切断。
- clone
func clone(): String
- 返回字符串的一份拷贝(由于 String 是不可变类型,通常不需要手动克隆)
- 常见用法总结
// 1. 判断空字符串 if (s.isEmpty()) { ... }
// 2. 安全搜索 if (let Some(idx) <- s.indexOf("key")) { println("Found at ${idx}") }
// 3. 分割与重组 let parts = "a:b:c".split(":") let joined = String.join(parts, delimiter: "-") // "a-b-c"
// 4. 裁剪用户输入 let input = " user@example.com ".trimAscii()
// 5. 路径处理 let filename = "path/to/file.cj" if (filename.endsWith(".cj")) { let name = filename.removeSuffix(".cj") }
// 6. 按行处理文本 for (line in text.lines()) { if (!line.isEmpty()) { processLine(line) } }
// 7. 字符串重复 let separator = "=" * 40 // "========================================"
// 8. 大小写不敏感比较 if (cmd.equalsIgnoreAsciiCase("quit")) { exit(0) }