Swift 字符串
字符串是编程里最常用的数据类型之一——显示文字、解析输入、处理 JSON 都离不开。Swift 的 String 设计独特(基于 Unicode 的扩展字形集群),本章带你掌握日常 80% 的字符串操作。
1. 字面量与多行字符串
// 字符串字面量
let greeting = "Hello"
let chinese = "你好, Swift"
// 空字符串
var empty1 = ""
var empty2 = String()
print(empty1.isEmpty) // true
// 多行字符串:三个双引号
let poem = """
床前明月光,
疑是地上霜。
举头望明月,
低头思故乡。
"""
print(poem)
// 多行里关掉换行,行尾写 \
let oneLine = """
静夜思 \/
李白
"""
// 实际是 "静夜思 李白"(没有换行)Swift 的多行字符串是同类语言里最优雅的——三个双引号包裹,内部的换行和缩进都会保留(末尾 \\ 表示"不要换行")。非常适合写 SQL、HTML、JSON 模板、长文案。
2. 字符串插值
插值是 Swift 字符串的核心。用 \\(表达式) 把任意值塞进字符串:
// 字符串插值:\(表达式)
let name = "小明"
let age = 18
let s1 = "我叫 \(name), 今年 \(age) 岁"
print(s1) // 我叫 小明, 今年 18 岁
// 插值里可以放任意表达式
let nums = [1, 2, 3]
print("数组: \(nums), 总和: \(nums.reduce(0, +))")
// 浮点数格式化(进阶)
let pi = 3.14159
print(String(format: "%.2f", pi)) // 3.14(借 Objective-C 的 NSString)
// 拼接:用 + 或 +=(性能已优化,放心用)
var message = "Hello"
message += ", Swift"
message.append("!")
print(message) // Hello, Swift!对比 C/Java 的 printf("%s is %d", name, age),Swift 的插值类型安全(编译期检查)、无占位符顺序问题、可读性高。新手可能不习惯反斜杠,但两三天就形成肌肉记忆。
3. 字符与 count
Swift 的 String 是 Character 集合,.count 返回字符数而不是字节数。这意味着一个汉字、一个 emoji 都算 1:
// 字符串是 Character 的集合
for ch in "Swift" {
print(ch) // S w i f t(逐字符)
}
// count 返回字符数,不是字节数
print("hello".count) // 5
print("你好".count) // 2(不是 UTF-8 的 6 字节)
print("👨👩👧".count) // 1(一个 emoji family 算一个字符)
// 字符 Character
let letter: Character = "A"
let heart: Character = "❤"
// 字符串拼接字符
var s = "ABC"
s.append("D")
print(s) // ABCD这点和 Python/JS 等语言不同(它们底层是 UTF-16/UTF-8,emoji 算 2)。Swift 的处理对"用户感知的字符数"更准确。
4. 包含、大小写、替换、分割
// 包含判断
let s = "Hello, Swift"
print(s.hasPrefix("Hello")) // true(前缀)
print(s.hasSuffix("Swift")) // true(后缀)
print(s.contains("ll")) // true(包含)
// 大小写转换
print(s.uppercased()) // HELLO, SWIFT
print(s.lowercased()) // hello, swift
print(s.capitalized) // Hello, Swift(每个单词首字母大写)
// 替换
let replaced = s.replacingOccurrences(of: "Swift", with: "World")
print(replaced) // Hello, World
// 去除首尾空白
let raw = " hi \n"
print(raw.trimmingCharacters(in: .whitespaces)) // hi
// 分割
let parts = "a,b,c".split(separator: ",")
print(parts) // ["a", "b", "c"]这些是日常 80% 的字符串操作需求,Swift 标准库基本都内置了。注意 split 返回的是 [Substring](子字符串数组),如果需要 [String] 要 .map(String.init)。
5. 切片与子串
Swift 字符串切片有一个反直觉的地方:不能用 Int 下标!必须用 String.Index。原因是不同字符宽度不同:
// 下标:用 String.Index,不能用 Int
let s = "Hello, Swift"
let i = s.startIndex // 起始下标
let j = s.index(i, offsetBy: 7) // 偏移 7
print(s[j]) // S("Swift" 的第一个字母)
// 切片:用范围运算符
let sub = s[j..<s.endIndex]
print(sub) // Swift
// 注意:Substring 共享原字符串内存,转 String 才独立
let independent = String(sub)
// 用下标取首末字符
if let first = s.first {
print(first) // H
}
if let last = s.last {
print(last) // t
}
// 插入、删除
var mutable = "ABC"
mutable.insert("D", at: mutable.endIndex)
print(mutable) // ABCD
mutable.remove(at: mutable.startIndex)
print(mutable) // BCD子串类型是 Substring 而非 String——它共享原字符串的内存(性能好),但短期使用。如果要长期保存,转成 String。
6. 为什么不能用 Int 下标
这是新手最常见的疑问。简短解释:
// 为什么 String 不能用 Int 下标?
// 因为 Swift 的 Character 是"扩展字形集群"
// emoji 比如 👨👩👧 由多个 Unicode 标量组成,占多个 UTF-16/UTF-8 单元
// 不同字符"宽度"不同,所以必须用 String.Index 来定位
let s = "abc👨👩👧def"
// s[3] // 编译错误:String 不能 Int 下标
let idx = s.index(s.startIndex, offsetBy: 3)
print(s[idx]) // 第 4 个字符(可能不是你预期的)
// 性能注意:offsetBy 是 O(n),逐字符扫描
// 处理超长字符串时,优先用 for-in 或 .enumerate()Swift 把"字符"定义为扩展字形集群——一个用户感知的字符(包括 emoji family、带音标的字母等)算一个 Character。这让 "abc👨👩👧def".count == 7,对用户体验是对的,但代价是不能用 Int 索引(因为不同字符"占的空间"不同)。
7. 性能注意点
- String 是值类型(struct),赋值即复制。但 Swift 用了写时复制(COW),实际不会立即拷贝,放心用。
- 大字符串频繁拼接:用
join或append(可变)比 + 更高效。 - offsetBy 是 O(n):超长字符串里反复
s.index(start, offsetBy: n)会变慢,优先用迭代器。 - 二进制数据用 Data:不要用 String 存原始字节。
8. 与 NSString 互操作
Swift 的 String 可以无缝转 Objective-C 的 NSString(Cocoa 框架常用)。比如用 replacingOccurrences、components(separatedBy:) 这些 NSString 风格 API:
let s = "a,b,c"
let parts = s.components(separatedBy: ",")
print(parts) // ["a", "b", "c"]NSString 的方法在 String 上直接能用,Swift 团队做了无缝桥接。日常推荐优先用 Swift 风格(split / map / filter),需要时再用 NSString 风格。
小结
Swift 字符串 = 字面量 + 插值 + Character 集合 + String.Index 切片 + NSString 桥接。最大的"坑"是不能用 Int 下标,但这是为了正确的 Unicode 支持。下一篇讲数组——比字符串简单得多。
← 上一篇 Swift 可选类型
下一篇 Swift 数组 →