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 > ?
          """
相关推荐
用户380341658829715 小时前
Flutter 迁 SwiftUI:AI 把页面"搬"过来只用了两周,我们补结构花了两个月
ios·swift
茶底世界之下18 小时前
四角变换后的最小包围视口,为什么不能只把宽高取整?
ios·swift
茶底世界之下2 天前
预乘 Alpha 图层合成为什么会越叠越暗?
架构·swift
茶底世界之下4 天前
时间线效果层,为什么不能只复用一个处理器?
ios·swift
Cc.Y4 天前
Swift深度链接的极简之道
ios·cocoa·swift
传奇开心果编程4 天前
【SwiftUI提高练中学】第9课 安全与隐私
swiftui·移动开发·swift·编程语言理论·开发范式·编程框架·声明式ui
茶底世界之下5 天前
动态切换 nearest / linear 时,为什么不能只替换 sampler
ios·swift
应用市场5 天前
把旧 Pixel 变成相册备份中转站(上):Mac 到安卓的照片传输工具设计——流式上传、sha256 校验、adb forward 与 Bonjour
android·macos·adb·kotlin·swift
东坡肘子5 天前
Swift Server,又多了一个 Google -- 肘子的 Swift 周报 #156
人工智能·swiftui·swift
打工仔折腾 AI6 天前
从零写一个CAD 05:以鼠标为中心的滚轮缩放,招法能复用但顺序不能反
开发语言·后端·python·性能优化·计算机外设·swift·ai agent 实战