如何在Swift中将“Index”转换为“Int”类型?

141

我希望将包含在字符串中的字母的索引转换为整数值。尝试阅读头文件,但是我找不到Index类型,尽管它似乎符合协议ForwardIndexType并具有方法(例如distanceTo)。

var letters = "abcdefg"
let index = letters.characters.indexOf("c")!

// ERROR: Cannot invoke initializer for type 'Int' with an argument list of type '(String.CharacterView.Index)'
let intValue = Int(index)  // I want the integer value of the index (e.g. 2)

感激所有的帮助。


实际上,我正在此时此刻下载Xcode 7.2。 - Christopher
68
在游乐场看到你想要的指数却难以转换成所需的种类是非常令人沮丧的事情。 - Adrian
4
Swift 3: let index = letters.characters.index(of: "c") 下一行 let int_index = letters.characters.distance(from: letters.startIndex, to: index)翻译: Swift 3: let index = letters.characters.index(of: "c") 意思是在字符串 letters 中找到字符 "c" 的位置。接下来的一行代码: let int_index = letters.characters.distance(from: letters.startIndex, to: index) 将字符 "c" 的位置 index 和字符串 letters 的起始位置 letters.startIndex 之间的距离转换为整数型。 - Viktor
40
苹果怎么回事!!!! - Borzh
4
已经读了几个小时,还是不知道发生了什么事情。这是怪异世界吗? - FranticRock
显示剩余3条评论
9个回答

112

编辑/更新:

Xcode 11 • Swift 5.1 或更高版本

extension StringProtocol {
    func distance(of element: Element) -> Int? { firstIndex(of: element)?.distance(in: self) }
    func distance<S: StringProtocol>(of string: S) -> Int? { range(of: string)?.lowerBound.distance(in: self) }
}

extension Collection {
    func distance(to index: Index) -> Int { distance(from: startIndex, to: index) }
}

extension String.Index {
    func distance<S: StringProtocol>(in string: S) -> Int { string.distance(to: self) }
}

Playground testing

let letters = "abcdefg"

let char: Character = "c"
if let distance = letters.distance(of: char) {
    print("character \(char) was found at position #\(distance)")   // "character c was found at position #2\n"
} else {
    print("character \(char) was not found")
}

let string = "cde"
if let distance = letters.distance(of: string) {
    print("string \(string) was found at position #\(distance)")   // "string cde was found at position #2\n"
} else {
    print("string \(string) was not found")
}

87
我很困惑,为什么他们不决定创建一个函数来返回数组元素的整数索引值。smh - MarksCode
8
并非所有字符都可以用一个字节存储。你应该花些时间阅读Swift字符串的文档。 - Leo Dabus
5
实际上,我正在尝试获取普通数组元素的整数索引值,而不是字符串。 - MarksCode
2
如果您的数组元素符合可比协议,那应该很简单。 - Leo Dabus
50
把简单的事情复杂化了 :( - Iulian Onofrei
显示剩余9条评论

47

1
谢谢!最佳答案!请用"myString"代替"number"。 - Hassan Taleb
已修复!在转换时忘记更改了。 - Eric33187
1
@Neph 它所做的就是获取字母的第一个索引(在你的情况下是最后一个),并将其存储在常量 i 中。然后它计算从字符串起始索引到 i 的距离。这些都是我使用的内置方法,Swift 提供了这些方法。 - Eric33187
将距离命名为“index”是具有误导性的。您无法使用它来对字符串进行下标引用。顺便说一句,Swift是一种类型推断语言。不需要显式设置结果类型。 - Leo Dabus
1
如果你正在寻找“Wo”,该怎么办? - Dan Selig
显示剩余3条评论

9

Swift 4

var str = "abcdefg"
let index = str.index(of: "c")?.encodedOffset // Result: 2

注意:如果字符串中包含相同的多个字符,则只会获取最接近左侧的一个。
var str = "abcdefgc"
let index = str.index(of: "c")?.encodedOffset // Result: 2

9
请勿使用 encodedOffset。encodedOffset 已被弃用,因为大多数常见用法都是不正确的。请尝试 "".index(of: "")?.encodedOffset // 16 - Leo Dabus
@LeoDabus,你说得没错,它已经被弃用了。但是你还是建议将其作为答案使用。正确的答案是:index_Of_YourStringVariable.utf16Offset(in: yourStringVariable)。 - TDesign
不,UTF16偏移量可能不是您想要的。 - Leo Dabus

4

encodedOffset 自 Swift 4.2 开始已被弃用。

弃用信息: encodedOffset 已被弃用,因为大多数常见用法是不正确的。使用 utf16Offset(in:) 可以实现相同的行为。

所以我们可以这样使用 utf16Offset(in:)

var str = "abcdefgc"
let index = str.index(of: "c")?.utf16Offset(in: str) // Result: 2

2
尝试 let str = "" let index = str.firstIndex(of: "")?.utf16Offset(in: str) // 结果:8 - Leo Dabus

3

当像这样搜索索引时

⛔️ guard let index = (positions.firstIndex { position <= $0 }) else {

如果您希望将其视为整数,编译器将其视为Array.Index。您需要向编译器提供一个提示,表明您需要一个整数。

✅ guard let index: Int = (positions.firstIndex { position <= $0 }) else {

3

Swift 5

您可以将其转换为字符数组,然后使用advanced(by:)将其转换为整数。

let myString = "Hello World"

if let i = Array(myString).firstIndex(of: "o") {
  let index: Int = i.advanced(by: 0)
  print(index) // Prints 4
}

0

这里有一个扩展程序,它可以让你以Int而不是String.Index值的形式访问子字符串的边界:

import Foundation

/// This extension is available at
/// https://gist.github.com/zackdotcomputer/9d83f4d48af7127cd0bea427b4d6d61b
extension StringProtocol {
    /// Access the range of the search string as integer indices
    /// in the rendered string.
    /// - NOTE: This is "unsafe" because it may not return what you expect if
    ///     your string contains single symbols formed from multiple scalars.
    /// - Returns: A `CountableRange<Int>` that will align with the Swift String.Index
    ///     from the result of the standard function range(of:).
    func countableRange<SearchType: StringProtocol>(
        of search: SearchType,
        options: String.CompareOptions = [],
        range: Range<String.Index>? = nil,
        locale: Locale? = nil
    ) -> CountableRange<Int>? {
        guard let trueRange = self.range(of: search, options: options, range: range, locale: locale) else {
            return nil
        }

        let intStart = self.distance(from: startIndex, to: trueRange.lowerBound)
        let intEnd = self.distance(from: trueRange.lowerBound, to: trueRange.upperBound) + intStart

        return Range(uncheckedBounds: (lower: intStart, upper: intEnd))
    }
}

请注意,这可能会导致一些奇怪的问题,这也是为什么苹果选择让它变得困难的原因。(虽然这是一个有争议的设计决策 - 通过使其变得困难来隐藏危险的事情...)

您可以在Apple的字符串文档中阅读更多信息,但tldr是这源于这些“索引”实际上是特定于实现的。它们代表了字符串在操作系统呈现后的索引,因此根据使用的Unicode规范版本,可以在不同的操作系统之间移动。这意味着通过索引访问值不再是常量时间操作,因为必须对数据运行UTF规范以确定字符串中的正确位置。如果您将其桥接到NSString或底层UTF标量的索引,这些索引也将无法与生成的值对齐。开发者要注意。


不需要再测量startIndex的距离。只需获取从下限到上限的距离并加上起始值即可。 - Leo Dabus
我还会在你的方法中添加选项。类似这样 func rangeInt<S: StringProtocol>(of aString: S, options: String.CompareOptions = []) -> Range<Int>? { guard let range = range(of: aString, options: options) else { return nil } let start = distance(from: startIndex, to: range.lowerBound) return start..<start+distance(from: range.lowerBound, to: range.upperBound) } - Leo Dabus
我可能会将方法名称更改为 countableRange 并返回 CountableRange - Leo Dabus
@LeoDabus 给出了非常好的建议。我已经添加了所有范围(of...)参数,所以现在你可以调用 countableRange(of:, options:, range:,locale:)。我已经更新了 Gist,也会更新这篇文章。 - Zack
我认为不需要使用range,因为你可以在一个子字符串上调用该方法。 - Leo Dabus
我同意可以在不使用那个参数的情况下获得功能,但我仍然认为尽可能地匹配苹果提供的API更好。 - Zack

0

要根据索引执行字符串操作,您不能使用传统的索引数字方法。因为swift.index是通过indices函数检索的,并且它不属于Int类型。即使String是字符数组,我们仍然无法按索引读取元素。

这很令人沮丧。

因此,要创建字符串中每个偶数字符的新子字符串,请查看以下代码。

let mystr = "abcdefghijklmnopqrstuvwxyz"
let mystrArray = Array(mystr)
let strLength = mystrArray.count
var resultStrArray : [Character] = []
var i = 0
while i < strLength {
    if i % 2 == 0 {
        resultStrArray.append(mystrArray[i])
      }
    i += 1
}
let resultString = String(resultStrArray)
print(resultString)

输出:acegikmoqsuwy

提前致谢


这可以通过使用过滤器轻松实现:var counter = 0let result = mystr.filter { _ in defer { counter += 1 } return counter.isMultiple(of: 2) } - Leo Dabus
如果您喜欢使用String.index,可以这样写var index = mystr.startIndexlet result = mystr.filter { _ in defer { mystr.formIndex(after: &index) } return mystr.distance(from: mystr.startIndex, to: index).isMultiple(of: 2) } - Leo Dabus

-1

如果你遇到了“数组越界”错误,你可以尝试这种方法。适用于 Swift 5。

extension String{

   func countIndex(_ char:Character) -> Int{
        var count = 0
        var temp = self
  
        for c in self{
            
            if c == char {
               
                //temp.remove(at: temp.index(temp.startIndex,offsetBy:count))
                //temp.insert(".", at: temp.index(temp.startIndex,offsetBy: count))
                
                return count

            }
            count += 1
         }
        return -1
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接