Swift 字符串与字符完全导读(一):从字面量到 Unicode 的实战之旅

前言

Swift 的 String 看起来"像 NSString 的弟弟",但骨子里是一套全新的 Unicode 抽象模型。

String 与 Character 的本质

  • String:由"扩展字形簇"(extended grapheme cluster)构成的有序集合。
  • Character:一个扩展字形簇,人类眼中的"一个字符",占用的字节数可变。
swift 复制代码
// 1 个 Character,由 2 个 Unicode 标量合成
let eAcute: Character = "é"                 // "é"
let eCombining: Character = "\u{65}\u{301}" // "e" + "́" 组合
print(eAcute == eCombining) // true,两者字形簇等价

字符串字面量:单行、多行、转义、扩展分隔符

单行字面量

swift 复制代码
let msg = "Hello, Swift" // 类型自动推断为 String

多行字面量

swift 复制代码
let html = """
           <div>
               <p>Hello</p>
           </div>
           """ // 缩进 4 空格会被自动去掉,因为闭合 """ 在最左侧第 12 列
           // 闭合"""左侧的空格会被删除,每一行左侧的同等长度的空格都会被删除

换行控制技巧

swift 复制代码
let sql = """
          SELECT * FROM user \
          WHERE age > 18
          """ // 反斜杠让源码换行,但字符串里无换行

转义序列

swift 复制代码
let special = "双引号:\",制表:\t,换行:\n,Unicode:\u{1F496}"

扩展分隔符(#)

场景:正则、JSON 模板里想保留原始反斜杠。

swift 复制代码
let raw = #"Raw \n still two characters"#
let needEscape = #"Use \#n to enable line break"#
print(raw)       // 输出:Raw \n still two characters
print(needEscape)// 输出:Use
                 //       to enable line break

多行 + 扩展分隔符

swift 复制代码
let mlRaw = #"""
            Line 1
            Line 2
            """#

空字符串的 3 种创建方式

swift 复制代码
let a = ""
let b = String()
let c = String("") // 与前两种等价
print(a.isEmpty) // true

可变性:let 与 var 的抉择

swift 复制代码
let immutable = "can't change"
// immutable += "!" // ❌ Compile-time error

var mutable = "hello"
mutable += ", world" // ✅

值类型:写时复制(COW)到底发生了什么

swift 复制代码
func foo(_ s: String) {
    var local = s   // 此时未复制,共享同一块缓冲区
    local += "!"    // 突变触发复制,O(n) 成本
    print(local)
}

底层优化:仅当本地突变或跨线程时才真正拷贝,因此作为入参传递时无需担心性能。

字符集合:遍历、提取、构造

swift 复制代码
let word = "Swift"
for ch in word {
    print(ch, terminator: "-") // S-w-i-f-t-
}

let single: Character = "A"
let fromChars = String([single, "B", "C"]) // "ABC"

字符串拼接的 5 种姿势

swift 复制代码
let left  = "Hello"
let right = "World"

// 1. 加法
let s1 = left + ", " + right

// 2. 加法赋值
var s2 = left
s2 += ", " + right

// 3. append(Character)
var s3 = left
s3.append(",")
s3.append(" ")
s3.append(Character(right)) // 仅当 right 长度=1 时安全

// 4. append(contentsOf: String)
var s4 = left
s4.append(contentsOf: ", \(right)")

// 5. 多行拼接注意最后一行换行
let goodStart = """
                Line 1
                Line 2
                """
let end       = """
                Line 3
                """
let merged = goodStart + end // 3 行,无意外合并

字符串插值:最灵活的"模板引擎"

swift 复制代码
let name = "Swift"
let year = 2025
let msg = "Hello, \(name)! In \(year + 1) we will rock."
print(msg) // Hello, Swift! In 2026 we will rock.

// 在扩展分隔符中使用插值
let rawLog = #"Level \#(name) recorded at \#(Date())"#

扩展场景:今天就能用上的 3 个小工具

彩色命令行日志

swift 复制代码
func log(_ info: String) {
    print(#"\u{1B}[32m[INFO]\#(info)\u{1B}[0m"#)
}
log("Server started") // 终端绿色输出

快速 Mock JSON

swift 复制代码
let userId = 9527
let json = #"""
           {"id": \#(userId), "name": "Alice"}
           """#
print(json) // 直接贴进 Postman 即可

多行 SQL 模板

swift 复制代码
let table = "user"
let sql = """
          SELECT *
          FROM \(table)
          WHERE status = 'active'
            AND created_at > ?
          """
相关推荐
m0_495562781 天前
Swift-GCD和NSOperation
ios·cocoa·swift
HarderCoder1 天前
Swift 并发:我到底该不该用 Actor?——一张决策图帮你拍板
swift
HarderCoder1 天前
深入理解 DispatchQueue.sync 的死锁陷阱:原理、案例与最佳实践
swift
东坡肘子1 天前
Skip Fuse现在对独立开发者免费! -- 肘子的 Swift 周报 #0110
android·swiftui·swift
Kapaseker2 天前
Swift 构建 Android 应用?它来了
ios·swift
HarderCoder2 天前
Swift 协议(Protocol)指南(四):协议扩展(Protocol Extension)——让“协议”自己也有默认实现
swift
HarderCoder2 天前
Swift 协议(Protocol)指南(三):Primary Associated Type、some/any 与泛型式协议实战
swift
HarderCoder2 天前
Swift 协议(Protocol)指南(二):关联类型、Self 约束与泛型递归,一次彻底搞懂
swift
HarderCoder2 天前
Swift 协议(Protocol)指南(一):从语法到实战
swift
HarderCoder2 天前
Swift TaskGroup 结果顺序踩坑指南:为什么返回顺序和创建顺序不一致,以及最通用的修复办法
swift