以下所有示例都使用
var str = "Hello, playground"
斯威夫特 4
Strings 在 Swift 4 中得到了相当大的改进。当你现在从 String 中获取一些子字符串时,你会得到一个 Substring 类型,而不是 String。为什么是这样?字符串是 Swift 中的值类型。这意味着如果您使用一个字符串来创建一个新字符串,则必须将其复制过来。这有利于稳定性(没有其他人会在你不知情的情况下更改它)但不利于效率。
另一方面,子字符串是对其来源的原始字符串的引用。这是来自documentation 的图片说明了这一点。
无需复制,因此使用效率更高。但是,假设您从一百万个字符的字符串中得到了一个十字符的子字符串。因为 Substring 正在引用 String,所以只要 Substring 存在,系统就必须保留整个 String。因此,每当您完成对子字符串的操作时,请将其转换为字符串。
let myString = String(mySubstring)
这将只复制子字符串,保存旧字符串的内存可以是reclaimed。子字符串(作为一种类型)是短暂的。
Swift 4 的另一个重大改进是字符串是集合(再次)。这意味着您可以对集合执行任何操作,也可以对字符串执行任何操作(使用下标、迭代字符、过滤器等)。
以下示例展示了如何在 Swift 中获取子字符串。
获取子字符串
您可以使用下标或许多其他方法(例如,prefix、suffix、split)从字符串中获取子字符串。不过,您仍然需要使用String.Index 而不是Int 范围索引。 (如果您需要帮助,请参阅 my other answer。)
字符串的开头
您可以使用下标(注意 Swift 4 单边范围):
let index = str.index(str.startIndex, offsetBy: 5)
let mySubstring = str[..<index] // Hello
或prefix:
let index = str.index(str.startIndex, offsetBy: 5)
let mySubstring = str.prefix(upTo: index) // Hello
甚至更简单:
let mySubstring = str.prefix(5) // Hello
字符串结束
使用下标:
let index = str.index(str.endIndex, offsetBy: -10)
let mySubstring = str[index...] // playground
或suffix:
let index = str.index(str.endIndex, offsetBy: -10)
let mySubstring = str.suffix(from: index) // playground
甚至更简单:
let mySubstring = str.suffix(10) // playground
请注意,当使用suffix(from: index) 时,我必须使用-10 从末尾倒数。仅使用 suffix(x) 时没有必要这样做,它只使用字符串的最后一个 x 字符。
字符串中的范围
我们再次在这里简单地使用下标。
let start = str.index(str.startIndex, offsetBy: 7)
let end = str.index(str.endIndex, offsetBy: -6)
let range = start..<end
let mySubstring = str[range] // play
将Substring 转换为String
别忘了,当你准备好保存你的子字符串时,你应该把它转换成String,这样旧字符串的内存就可以被清理掉了。
let myString = String(mySubstring)
使用Int 索引扩展?
在阅读了 Airspeed Velocity 和 Ole Begemann 的文章 Strings in Swift 3 后,我犹豫是否要使用基于 Int 的索引扩展。尽管在 Swift 4 中,字符串是集合,但 Swift 团队故意没有使用 Int 索引。它仍然是String.Index。这与由不同数量的 Unicode 代码点组成的 Swift 字符有关。必须为每个字符串唯一地计算实际索引。
我不得不说,我希望 Swift 团队在未来能找到一种方法来抽象出 String.Index。但在他们之前,我选择使用他们的 API。它帮助我记住字符串操作不仅仅是简单的Int 索引查找。