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


89

我想将字符串中包含的字母的索引转换为整数值。尝试读取头文件,但找不到的类型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和swift 2.x吗?
aaisataev 2015年

实际上,此刻我正在下载Xcode 7.2。
Christopher

32
没有比在操场上看到想要盯着你的索引更令人沮丧的事情了,将索引转换为所需类型是一个巨大的PITA。我宁愿把我的啄木鸟关在门上。
阿德里安

1
斯威夫特3:let index = letters.characters.index(of: "c") 下一线let int_index = letters.characters.distance(from: letters.startIndex, to: index)
维克多

8
苹果WTF !!!!!!
Borzh

Answers:


87

编辑/更新:

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) }
}

游乐场测试

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")
}

47
我很困惑,为什么他们不决定创建一个返回数组元素的整数索引的函数
。smh

6
并非所有字符都可以存储在单个字节中。您应该花些时间阅读Swift String文档
Leo Dabus


4
我正在尝试获取常规数组元素而不是字符串的整数值索引。
MarksCode

28
使简单的事情变得不必要的复杂:(
Iulian Onofrei

4

斯威夫特4

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

注意:如果String包含相同的多个字符,则只会从左边获得最接近的一个字符

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

5
不要使用encodedOffset。encodingOffset已弃用:encodedOffset已弃用,因为最常见的用法是不正确的。试试"🇺🇸🇺🇸🇧🇷".index(of: "🇧🇷")?.encodedOffset // 16
Leo Dabus,

@LeoDabus,您正确地指出它已被弃用。但是您还是建议使用它作为答案。正确答案是:index_Of_YourStringVariable.utf16Offset(in:yourStringVariable)
TDesign

不,它可能不是您想要的UTF16偏移量
Leo Dabus

1

encodedOffset已从Swift 4.2中弃用。

弃用消息: encodedOffset已被弃用,因为大多数常用用法都不正确。使用utf16Offset(in:)以达到相同的行为。

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

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

1
试试let str = "🇺🇸🇺🇸🇧🇷" let index = str.firstIndex(of: "🇧🇷")?.utf16Offset(in: str)//结果:8
Leo Dabus

0

要基于index执行字符串操作,您不能使用传统的索引数字方法来执行。因为swift.index是由indexs函数检索的,并且不是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 = 0 let result = mystr.filter { _ in defer { counter += 1 } return counter.isMultiple(of: 2) }
Leo Dabus

如果您更喜欢使用String.indexvar index = mystr.startIndex let result = mystr.filter { _ in defer { mystr.formIndex(after: &index) } return mystr.distance(from: mystr.startIndex, to: index).isMultiple(of: 2) }
Leo Dabus

0

这是一个扩展,使您可以使用Ints而不是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选择加倍努力的原因。(尽管这是一个值得商design的设计决策-通过使其变得困难来隐藏危险的东西...)

您可以从AppleString文档中了解更多信息,但tldr的原因是这些“索引”实际上是特定于实现的。它们代表OS渲染后到字符串中的索引,因此可以根据所使用的Unicode规范版本在OS与OS之间切换。这意味着按索引访问值不再是固定时间的操作,因为必须对数据运行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的所有建议都很好。实际上,我添加了所有range(of ...)参数,因此您现在可以调用countableRange(of :, options :, range:,locale :)。更新了要点,也将更新此帖子。
扎克

考虑到您可以在子字符串上调用该方法,我不需要设置范围
Leo Dabus
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.