查找Swift String中的字符索引

Mat*_*ing 194 string swift

现在是承认失败的时候了......

在Objective-C中,我可以使用类似的东西:

NSString* str = @"abcdefghi";
[str rangeOfString:@"c"].location; // 2
Run Code Online (Sandbox Code Playgroud)

在Swift中,我看到类似的东西:

var str = "abcdefghi"
str.rangeOfString("c").startIndex
Run Code Online (Sandbox Code Playgroud)

...但是这只是给了我一个String.Index,我可以使用它来下标回原始字符串,但不从中提取位置.

FWIW,String.Index有一个私人ivar _position,它具有正确的价值.我只是看不出它是如何曝光的.

我知道我可以轻松地将它添加到String中.我对这个新API中缺少的东西更感兴趣.

Sul*_*han 238

您不是唯一一个找不到解决方案的人.

String没有实现RandomAccessIndexType.可能是因为它们启用了具有不同字节长度的字符.这就是我们必须使用string.characters.count(countcountElements在Swift 1.x中)获取字符数的原因.这也适用于职位.的_position可能是一个索引字节的原始阵列,他们不希望公开这一点.这String.Index是为了保护我们不要访问字符中间的字节.

这意味着你得到任何索引必须从创建String.startIndexString.endIndex(String.Index工具BidirectionalIndexType).可以使用successorpredecessor方法创建任何其他索引.

现在为了帮助我们索引,有一组方法(Swift 1.x中的函数):

Swift 4.x

let text = "abc"
let index2 = text.index(text.startIndex, offsetBy: 2) //will call succ 2 times
let lastChar: Character = text[index2] //now we can index!

let characterIndex2 = text.index(text.startIndex, offsetBy: 2)
let lastChar2 = text[characterIndex2] //will do the same as above

let range: Range<String.Index> = text.range(of: "b")!
let index: Int = text.distance(from: text.startIndex, to: range.lowerBound)
Run Code Online (Sandbox Code Playgroud)

Swift 3.0

let text = "abc"
let index2 = text.index(text.startIndex, offsetBy: 2) //will call succ 2 times
let lastChar: Character = text[index2] //now we can index!

let characterIndex2 = text.characters.index(text.characters.startIndex, offsetBy: 2)
let lastChar2 = text.characters[characterIndex2] //will do the same as above

let range: Range<String.Index> = text.range(of: "b")!
let index: Int = text.distance(from: text.startIndex, to: range.lowerBound)
Run Code Online (Sandbox Code Playgroud)

Swift 2.x

let text = "abc"
let index2 = text.startIndex.advancedBy(2) //will call succ 2 times
let lastChar: Character = text[index2] //now we can index!
let lastChar2 = text.characters[index2] //will do the same as above

let range: Range<String.Index> = text.rangeOfString("b")!
let index: Int = text.startIndex.distanceTo(range.startIndex) //will call successor/predecessor several times until the indices match
Run Code Online (Sandbox Code Playgroud)

Swift 1.x

let text = "abc"
let index2 = advance(text.startIndex, 2) //will call succ 2 times
let lastChar: Character = text[index2] //now we can index!

let range = text.rangeOfString("b")
let index: Int = distance(text.startIndex, range.startIndex) //will call succ/pred several times
Run Code Online (Sandbox Code Playgroud)

使用String.Index是很麻烦但使用包装器按整数索引(请参阅/sf/answers/1760685671/)是危险的,因为它隐藏了真正索引的低效率.

请注意,Swift索引实现存在以下问题:为一个字符串创建的索引/范围无法可靠地用于其他字符串,例如:

Swift 2.x

let text: String = "abc"
let text2: String = ""

let range = text.rangeOfString("b")!

//can randomly return a bad substring or throw an exception
let substring: String = text2[range]

//the correct solution
let intIndex: Int = text.startIndex.distanceTo(range.startIndex)
let startIndex2 = text2.startIndex.advancedBy(intIndex)
let range2 = startIndex2...startIndex2

let substring: String = text2[range2]
Run Code Online (Sandbox Code Playgroud)

Swift 1.x

let text: String = "abc"
let text2: String = ""

let range = text.rangeOfString("b")

//can randomly return nil or a bad substring 
let substring: String = text2[range] 

//the correct solution
let intIndex: Int = distance(text.startIndex, range.startIndex)    
let startIndex2 = advance(text2.startIndex, intIndex)
let range2 = startIndex2...startIndex2

let substring: String = text2[range2]  
Run Code Online (Sandbox Code Playgroud)

  • @Zaph每个`Collection`都有一个`typealias IndexType`.对于数组,它被定义为`Int`,对于`String`,它被定义为`String.Index`.数组和字符串也可以使用范围(以创建子数组和子字符串).范围是一种特殊类型`Range <T>`.对于字符串,它是`Range <String.Index>`,对于数组`Range <Int>`. (5认同)

Pas*_*cal 86

Swift 3.0使这个更冗长:

let string = "Hello.World"
let needle: Character = "."
if let idx = string.characters.index(of: needle) {
    let pos = string.characters.distance(from: string.startIndex, to: idx)
    print("Found \(needle) at position \(pos)")
}
else {
    print("Not found")
}
Run Code Online (Sandbox Code Playgroud)

延期:

extension String {
    public func index(of char: Character) -> Int? {
        if let idx = characters.index(of: char) {
            return characters.distance(from: startIndex, to: idx)
        }
        return nil
    }
}
Run Code Online (Sandbox Code Playgroud)

Swift 2.0中,这变得更容易:

let string = "Hello.World"
let needle: Character = "."
if let idx = string.characters.indexOf(needle) {
    let pos = string.startIndex.distanceTo(idx)
    print("Found \(needle) at position \(pos)")
}
else {
    print("Not found")
}
Run Code Online (Sandbox Code Playgroud)

延期:

extension String {
    public func indexOfCharacter(char: Character) -> Int? {
        if let idx = self.characters.indexOf(char) {
            return self.startIndex.distanceTo(idx)
        }
        return nil
    }
}
Run Code Online (Sandbox Code Playgroud)

Swift 1.x实现:

对于纯Swift解决方案,可以使用:

let string = "Hello.World"
let needle: Character = "."
if let idx = find(string, needle) {
    let pos = distance(string.startIndex, idx)
    println("Found \(needle) at position \(pos)")
}
else {
    println("Not found")
}
Run Code Online (Sandbox Code Playgroud)

作为扩展String:

extension String {
    public func indexOfCharacter(char: Character) -> Int? {
        if let idx = find(self, char) {
            return distance(self.startIndex, idx)
        }
        return nil
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 字符已弃用! (2认同)

小智 23

extension String {

    // MARK: - sub String
    func substringToIndex(index:Int) -> String {
        return self.substringToIndex(advance(self.startIndex, index))
    }
    func substringFromIndex(index:Int) -> String {
        return self.substringFromIndex(advance(self.startIndex, index))
    }
    func substringWithRange(range:Range<Int>) -> String {
        let start = advance(self.startIndex, range.startIndex)
        let end = advance(self.startIndex, range.endIndex)
        return self.substringWithRange(start..<end)
    }

    subscript(index:Int) -> Character{
        return self[advance(self.startIndex, index)]
    }
    subscript(range:Range<Int>) -> String {
        let start = advance(self.startIndex, range.startIndex)
            let end = advance(self.startIndex, range.endIndex)
            return self[start..<end]
    }


    // MARK: - replace
    func replaceCharactersInRange(range:Range<Int>, withString: String!) -> String {
        var result:NSMutableString = NSMutableString(string: self)
        result.replaceCharactersInRange(NSRange(range), withString: withString)
        return result
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 想到这样做,但我认为它隐藏了字符串访问的语义是一个问题.想象一下,创建一个用于访问链接列表的API,它看起来就像数组的API.人们希望编写非常低效的代码. (7认同)

VYT*_*VYT 16

我找到了swift2的这个解决方案:

var str = "abcdefghi"
let indexForCharacterInString = str.characters.indexOf("c") //returns 2
Run Code Online (Sandbox Code Playgroud)


Con*_*nor 8

我不确定如何从String.Index中提取位置,但是如果你愿意依赖于某些Objective-C框架,你可以使用与之前相同的方式桥接到objective-c.

"abcdefghi".bridgeToObjectiveC().rangeOfString("c").location
Run Code Online (Sandbox Code Playgroud)

似乎某些NSString方法尚未(或可能不会)移植到String.包含也会浮现在脑海中.


Yan*_*eph 8

这是一个干净的String扩展,它回答了这个问题:

斯威夫特3:

extension String {
    var length:Int {
        return self.characters.count
    }

    func indexOf(target: String) -> Int? {

        let range = (self as NSString).range(of: target)

        guard range.toRange() != nil else {
            return nil
        }

        return range.location

    }
    func lastIndexOf(target: String) -> Int? {



        let range = (self as NSString).range(of: target, options: NSString.CompareOptions.backwards)

        guard range.toRange() != nil else {
            return nil
        }

        return self.length - range.location - 1

    }
    func contains(s: String) -> Bool {
        return (self.range(of: s) != nil) ? true : false
    }
}
Run Code Online (Sandbox Code Playgroud)

Swift 2.2:

extension String {    
    var length:Int {
        return self.characters.count
    }

    func indexOf(target: String) -> Int? {

        let range = (self as NSString).rangeOfString(target)

        guard range.toRange() != nil else {
            return nil
        }

        return range.location

    }
    func lastIndexOf(target: String) -> Int? {



        let range = (self as NSString).rangeOfString(target, options: NSStringCompareOptions.BackwardsSearch)

        guard range.toRange() != nil else {
            return nil
        }

        return self.length - range.location - 1

    }
    func contains(s: String) -> Bool {
        return (self.rangeOfString(s) != nil) ? true : false
    }
}
Run Code Online (Sandbox Code Playgroud)


San*_*eep 6

您还可以像这样在单个字符串中找到字符的索引,

extension String {

  func indexes(of character: String) -> [Int] {

    precondition(character.count == 1, "Must be single character")

    return self.enumerated().reduce([]) { partial, element  in
      if String(element.element) == character {
        return partial + [element.offset]
      }
      return partial
    }
  }

}
Run Code Online (Sandbox Code Playgroud)

在[String.Distance]中给出结果,即。[Int],像

"apple".indexes(of: "p") // [1, 2]
"element".indexes(of: "e") // [0, 2, 4]
"swift".indexes(of: "j") // []
Run Code Online (Sandbox Code Playgroud)


Log*_*gan 5

如果您想使用熟悉的NSString,可以明确声明它:

var someString: NSString = "abcdefghi"

var someRange: NSRange = someString.rangeOfString("c")
Run Code Online (Sandbox Code Playgroud)

我不确定如何在Swift中这样做.


Vin*_*nso 5

斯威夫特5.0

public extension String {  
  func indexInt(of char: Character) -> Int? {
    return firstIndex(of: char)?.utf16Offset(in: self)
  }
}
Run Code Online (Sandbox Code Playgroud)

迅捷4.0

public extension String {  
  func indexInt(of char: Character) -> Int? {
    return index(of: char)?.encodedOffset        
  }
}
Run Code Online (Sandbox Code Playgroud)


Vik*_*tor 5

斯威夫特 5

查找子串的索引

let str = "abcdecd"
if let range: Range<String.Index> = str.range(of: "cd") {
    let index: Int = str.distance(from: str.startIndex, to: range.lowerBound)
    print("index: ", index) //index: 2
}
else {
    print("substring not found")
}
Run Code Online (Sandbox Code Playgroud)

查找字符索引

let str = "abcdecd"
if let firstIndex = str.firstIndex(of: "c") {
    let index: Int = str.distance(from: str.startIndex, to: firstIndex)
    print("index: ", index)   //index: 2
}
else {
    print("symbol not found")
}
Run Code Online (Sandbox Code Playgroud)