String.Index如何在Swift中工作

Sur*_*gch 92 string indexing swift

我一直在使用Swift 3更新我的一些旧代码和答案,但是当我使用Swift Strings和Indexing时,理解事情是一件很痛苦的事情.

具体来说,我尝试以下方法:

let str = "Hello, playground"
let prefixRange = str.startIndex..<str.startIndex.advancedBy(5) // error
Run Code Online (Sandbox Code Playgroud)

第二行给我以下错误

'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)
Run Code Online (Sandbox Code Playgroud)

起初这些让我很困惑所以我开始玩它们直到我理解它们.我在下面添加一个答案来说明它们是如何使用的.

Sur*_*gch 239

在此输入图像描述

以下所有示例均使用

var str = "Hello, playground"
Run Code Online (Sandbox Code Playgroud)

startIndexendIndex

  • 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"
Run Code Online (Sandbox Code Playgroud)

使用Swift 4的单面范围,可将范围简化为以下形式之一.

let range = str.startIndex...
let range = ..<str.endIndex
Run Code Online (Sandbox Code Playgroud)

为了清楚起见,我将使用以下示例中的完整表单,但为了便于阅读,您可能希望在代码中使用单侧范围.

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"
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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
}
Run Code Online (Sandbox Code Playgroud)

如果77代替了偏移量7,那么该if语句将被跳过.

为什么需要String.Index?

这将是很多容易使用的Int弦乐指数.你必须String.Index为每个String 创建一个新的原因是Swift中的字符在引擎盖下的长度并不完全相同.单个Swift字符可能由一个,两个甚至更多Unicode代码点组成.因此,每个唯一的String必须计算其Character的索引.

它可能隐藏在Int索引扩展后面的这种复杂性,但我不愿意这样做.提醒实际发生的事情是件好事.

  • 为什么`startIndex`不是0? (13认同)
  • @RoboRobok:因为Swift使用由"字形簇"组成的Unicode字符,所以Swift不使用整数来表示索引位置.假设你的第一个角色是'é`.它实际上由`e`加上'\ u {301}`Unicode表示法组成.如果你使用零索引,你会得到'e`或重音("严重")字符,而不是构成`é`的整个集群.使用`startIndex`可确保您获得任何字符的整个字形集群. (11认同)
  • 在 Swift 4.0 中,每个 Unicode 字符都按 1 计数。例如:"‍".count // 现在:1,之前:2 (3认同)
  • 除了构建虚拟字符串并在其上使用.index方法之外,如何从整数构造“ String.Index”?我不知道我是否缺少什么,但是文档什么也没说。 (3认同)
  • @sudo,在用整数构造`String.Index`时必须要小心一点,因为每个Swift`Character`不一定等于用整数表示的相同含义。也就是说,您可以将一个整数传递到`offsetBy`参数中以创建一个`String.Index`。但是,如果您没有String,那么就无法构造String.Index(因为Swift仅在知道字符串中的先前字符是什么的情况下才可以计算索引)。如果更改字符串,则必须重新计算索引。您不能在两个不同的字符串上使用相同的`String.Index`。 (3认同)