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. 性能注意点

8. 与 NSString 互操作

Swift 的 String 可以无缝转 Objective-C 的 NSString(Cocoa 框架常用)。比如用 replacingOccurrencescomponents(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 数组

✈️💬