引言:每个Swift开发者都会遇到的“陷阱”
\n\n想象一下这个场景:你自信地编写Swift代码,一切进展顺利,然后你需要从字符串中提取前三个字符。你不假思索地写下类似myString[0..<3]的代码——然后得到了……一个编译错误。熟悉吗?
欢迎来到Swift中的String substring的世界。这是让新手甚至从其他语言转过来的经验丰富的开发者感到痛苦的话题之一。在Stack Overflow上,“How does String substring work in Swift”这个问题获得了数千次浏览——这并非偶然。
\n\n在本文中,我们将探讨为什么Swift在处理子字符串时如此“奇怪”,如何正确使用Swift中的substring,以及最新版本中出现了哪些新功能。我们开始吧!
\n\n为什么Swift不允许你直接剪切字符串的一部分?
\n\n在大多数语言(Python、JavaScript、C#)中,字符串只是一个字符数组。想要子字符串?写str[0:3]就能得到结果。在Swift中,情况完全不同。
原因在于Unicode的处理方式。Swift中的字符串不仅仅是字节或ASCII字符。它们由字素簇(grapheme clusters)组成。用户看到的一个字符(例如“é”或“👍🏽”)可能占用多个Character甚至多个Unicode.Scalar。想象一下,你在这样一个簇的中间截断字符串——你会得到损坏的数据。
因此,Swift使用索引(String.Index),而不是整数。这确保了你永远不会将一个字符切成两半。
基础:String.Index和简单的子字符串提取
\n\n让我们从一个基本示例开始。假设我们有一个字符串:
\n\nlet fullString = "Привет, Swift!"\n// 想要提取单词 "Swift"\nlet startIndex = fullString.index(fullString.startIndex, offsetBy: 8)\nlet endIndex = fullString.index(fullString.startIndex, offsetBy: 13)\nlet substring = fullString[startIndex..<endIndex]\nprint(substring) // 输出: "Swift"\n\n请注意:substring的类型是Substring,而不是String。这是一个重要的区别。Substring是原始字符串的一个“窗口”。它不复制数据,只是引用原始字符串的一部分内存。这非常高效,但如果原始字符串很大而子字符串很小,可能会导致内存泄漏。
要将Substring转换为完整的String,只需使用初始化器:
let newString = String(substring)\nprint(newString) // "Swift"\n\n语法糖:RangeExpression
\n\n每次都写index(offsetBy:)很繁琐。幸运的是,Swift允许使用RangeExpression的更简短形式。
运算符 ... (ClosedRange)
\nlet str = "Hello, World!"\nif let range = str.range(of: "World") {\n let worldSubstring = str[range]\n print(worldSubstring) // "World"\n}\n\n部分范围 (Partial Range)
\n想要获取某个索引之前或之后的所有内容?很简单:
\nlet text = "Swift Programming"\nlet prefix = text[..<text.index(text.startIndex, offsetBy: 5)]\nprint(prefix) // "Swift"\n\nlet suffix = text[text.index(text.startIndex, offsetBy: 6)...]\nprint(suffix) // "Programming"\n\n请注意:prefix和suffix也是Substring类型。
内置方法:prefix, suffix, dropFirst, dropLast
\n\nSwift提供了便捷的方法,省去了手动计算索引的麻烦。它们返回Substring。
- \n
str.prefix(5)— 前5个字符。 \n str.suffix(3)— 后3个字符。