我一直在使用Swift 3更新我的一些旧代码和答案,但是当我使用Swift Strings和Indexing时,理解事情一直很痛苦。
具体来说,我正在尝试以下方法:
let str = "Hello, playground"
let prefixRange = str.startIndex..<str.startIndex.advancedBy(5) // error
第二行给出了以下错误
'advancedBy'不可用:要将索引推进n步,请在生成索引的CharacterView实例上调用'index(_:offsetBy :)'。
我看到String
有以下方法。
str.index(after: String.Index)
str.index(before: String.Index)
str.index(String.Index, offsetBy: String.IndexDistance)
str.index(String.Index, offsetBy: String.IndexDistance, limitedBy: String.Index)
起初这些让我很困惑所以我开始玩它们直到我理解它们为止。我在下面添加一个答案来说明它们是如何使用的。
答案 0 :(得分:215)
以下所有示例均使用
var str = "Hello, playground"
startIndex
和endIndex
startIndex
是第一个字符的索引endIndex
是后最后一个字符的索引。实施例
// character
str[str.startIndex] // H
str[str.endIndex] // error: after last character
// range
let range = str.startIndex..<str.endIndex
str[range] // "Hello, playground"
使用Swift 4&#39; one-sided ranges,范围可以简化为以下形式之一。
let range = str.startIndex...
let range = ..<str.endIndex
为了清楚起见,我将使用以下示例中的完整表单,但为了便于阅读,您可能希望在代码中使用单侧范围。
after
如:index(after: String.Index)
after
指的是直接在给定索引之后的字符索引。实施例
// character
let index = str.index(after: str.startIndex)
str[index] // "e"
// range
let range = str.index(after: str.startIndex)..<str.endIndex
str[range] // "ello, playground"
before
如:index(before: String.Index)
before
指的是直接在给定索引之前的字符索引。实施例
// character
let index = str.index(before: str.endIndex)
str[index] // d
// range
let range = str.startIndex..<str.index(before: str.endIndex)
str[range] // Hello, playgroun
offsetBy
如:index(String.Index, offsetBy: String.IndexDistance)
offsetBy
值可以是正数或负数,并从给定的索引开始。虽然它属于String.IndexDistance
类型,但您可以为其指定Int
。实施例
// character
let index = str.index(str.startIndex, offsetBy: 7)
str[index] // p
// range
let start = str.index(str.startIndex, offsetBy: 7)
let end = str.index(str.endIndex, offsetBy: -6)
let range = start..<end
str[range] // play
limitedBy
如:index(String.Index, offsetBy: String.IndexDistance, limitedBy: String.Index)
limitedBy
对于确保偏移量不会导致索引超出范围非常有用。这是一个有界的指数。由于偏移量可能超出限制,因此此方法返回Optional。如果索引超出范围,则返回nil
。实施例
// character
if let index = str.index(str.startIndex, offsetBy: 7, limitedBy: str.endIndex) {
str[index] // p
}
如果偏移量为77
而不是7
,则会跳过if
语句。
对于字符串,使用Int
索引会更容易 。你必须为每个String创建一个新的String.Index
的原因是Swift中的字符长度不同。单个Swift字符可能由一个,两个甚至更多Unicode代码点组成。因此,每个唯一的String必须计算其Character的索引。
可能会隐藏Int索引扩展后面的这种复杂性,但我不愿意这样做。提醒实际发生的事情是件好事。
答案 1 :(得分:1)
我感谢这个问题以及所有相关信息。我想到的是关于String.Index的一个问题和一个答案。
我正在尝试查看是否有O(1)方式访问字符串内的子字符串(或字符),因为如果您查看string.index(startIndex,offsetBy:1)的速度为O(n),索引函数的定义。当然我们可以做类似的事情:
let characterArray = Array(string)
然后访问characterArray中的任何位置,但是它的空间复杂度为n
=字符串的长度O(n),因此有点浪费空间。
我一直在看Xcode中的Swift.String文档,并且有一个冻结的公共结构Index
。我们可以初始化为:
let index = String.Index(encodedOffset: 0)
然后只需访问或打印String对象中的任何索引,如下所示:
print(string[index])
注意:请注意不要越界`
这很有效,但是这样做的运行时间和空间复杂度是多少?好吗?
答案 2 :(得分:0)
在tableViewController内部创建一个UITextView。我使用了函数:textViewDidChange,然后检查了返回键输入。 然后如果检测到回车键输入,则删除回车键的输入并关闭键盘。
func textViewDidChange(_ textView: UITextView) {
tableView.beginUpdates()
if textView.text.contains("\n"){
textView.text.remove(at: textView.text.index(before: textView.text.endIndex))
textView.resignFirstResponder()
}
tableView.endUpdates()
}
答案 3 :(得分:-1)
func change(string: inout String) {
var character: Character = .normal
enum Character {
case space
case newLine
case normal
}
for i in stride(from: string.count - 1, through: 0, by: -1) {
// first get index
let index: String.Index?
if i != 0 {
index = string.index(after: string.index(string.startIndex, offsetBy: i - 1))
} else {
index = string.startIndex
}
if string[index!] == "\n" {
if character != .normal {
if character == .newLine {
string.remove(at: index!)
} else if character == .space {
let number = string.index(after: string.index(string.startIndex, offsetBy: i))
if string[number] == " " {
string.remove(at: number)
}
character = .newLine
}
} else {
character = .newLine
}
} else if string[index!] == " " {
if character != .normal {
string.remove(at: index!)
} else {
character = .space
}
} else {
character = .normal
}
}
// startIndex
guard string.count > 0 else { return }
if string[string.startIndex] == "\n" || string[string.startIndex] == " " {
string.remove(at: string.startIndex)
}
// endIndex - here is a little more complicated!
guard string.count > 0 else { return }
let index = string.index(before: string.endIndex)
if string[index] == "\n" || string[index] == " " {
string.remove(at: index)
}
}
答案 4 :(得分:-2)
Swift 4完整解决方案:
OffsetIndexableCollection(使用Int索引的字符串)
https://github.com/frogcjn/OffsetIndexableCollection-String-Int-Indexable-