Swift:如何从字符的开头到最后一个索引获取子字符串

Jas*_*ker 103 ios swift

我想学习将字符串转换为另一个字符串的最佳/最简单的方法,但只有一个子集,从头开始并转到字符的最后一个索引.

例如,将"www.stackoverflow.com"转换为"www.stackoverflow".什么代码片段可以做到这一点,并且是最快速的?(我希望这不会带来争论,但我找不到关于如何处理Swift中的子串的好教训.

fpg*_*503 195

只是向后访问

最好的方法是substringToIndex结合使用endIndex属性和advance全局函数.

var string1 = "www.stackoverflow.com"

var index1 = advance(string1.endIndex, -4)

var substring1 = string1.substringToIndex(index1)
Run Code Online (Sandbox Code Playgroud)

寻找从后面开始的字符串

使用rangeOfString并设置options为.BackwardsSearch

var string2 = "www.stackoverflow.com"

var index2 = string2.rangeOfString(".", options: .BackwardsSearch)?.startIndex

var substring2 = string2.substringToIndex(index2!)
Run Code Online (Sandbox Code Playgroud)

没有扩展,纯粹惯用的Swift

Swift 2.0

advance现在是一部分,Index被称为advancedBy.你这样做:

var string1 = "www.stackoverflow.com"

var index1 = string1.endIndex.advancedBy(-4)

var substring1 = string1.substringToIndex(index1)
Run Code Online (Sandbox Code Playgroud)

Swift 3.0

你不能调用advancedBya,String因为它有可变大小的元素.你必须使用index(_, offsetBy:).

var string1 = "www.stackoverflow.com"

var index1 = string1.index(string1.endIndex, offsetBy: -4)

var substring1 = string1.substring(to: index1)
Run Code Online (Sandbox Code Playgroud)

很多东西都被重命名了.这些案例是用camelCase编写的,startIndex成了lowerBound.

var string2 = "www.stackoverflow.com"

var index2 = string2.range(of: ".", options: .backwards)?.lowerBound

var substring2 = string2.substring(to: index2!)
Run Code Online (Sandbox Code Playgroud)

另外,我不建议强行展开index2.您可以使用可选的绑定或map.就个人而言,我更喜欢使用map:

var substring3 = index2.map(string2.substring(to:))
Run Code Online (Sandbox Code Playgroud)

斯威夫特4

Swift 3版本仍然有效,但现在您可以使用索引范围的下标:

let string1 = "www.stackoverflow.com"

let index1 = string1.index(string1.endIndex, offsetBy: -4)

let substring1 = string1[..<index1]
Run Code Online (Sandbox Code Playgroud)

第二种方法保持不变:

let string2 = "www.stackoverflow.com"

let index2 = string2.range(of: ".", options: .backwards)?.lowerBound

let substring3 = index2.map(string2.substring(to:))
Run Code Online (Sandbox Code Playgroud)

  • 真的没有办法简单地做到这一点吗? (79认同)
  • @devios都是真的.我很感激Swift与Objective-C相比,但恕我直言,导致这个疯狂尴尬的代码的担忧与99%的代码和编码器无关.更糟糕的是,我们最终会自己编写糟糕的便利扩展.如果常规开发人员无法理解这些更高级别的问题,那么他们至少能够编写一个ad-hoc API来处理我们制作软件的行人问题.转换为"NSString"的倾向显然也很糟糕,因为最终我们都希望摆脱基础(遗留)类.所以....更多的咆哮! (12认同)
  • 在 Swift 4 中,你可以做 `string1[..&lt;index1]`。不需要`string1.startIndex`。 (2认同)

ser*_*zhd 24

Swift 3,XCode 8

func lastIndexOfCharacter(_ c: Character) -> Int? {
    return range(of: String(c), options: .backwards)?.lowerBound.encodedOffset
}
Run Code Online (Sandbox Code Playgroud)

自从advancedBy(Int)Swift 3使用String的方法以来就消失了index(String.Index, Int).String使用substring和friends 查看此扩展:

public extension String {

    //right is the first encountered string after left
    func between(_ left: String, _ right: String) -> String? {
        guard let leftRange = range(of: left), let rightRange = range(of: right, options: .backwards)
        , leftRange.upperBound <= rightRange.lowerBound
            else { return nil }

        let sub = self.substring(from: leftRange.upperBound)
        let closestToLeftRange = sub.range(of: right)!
        return sub.substring(to: closestToLeftRange.lowerBound)
    }

    var length: Int {
        get {
            return self.characters.count
        }
    }

    func substring(to : Int) -> String {
        let toIndex = self.index(self.startIndex, offsetBy: to)
        return self.substring(to: toIndex)
    }

    func substring(from : Int) -> String {
        let fromIndex = self.index(self.startIndex, offsetBy: from)
        return self.substring(from: fromIndex)
    }

    func substring(_ r: Range<Int>) -> String {
        let fromIndex = self.index(self.startIndex, offsetBy: r.lowerBound)
        let toIndex = self.index(self.startIndex, offsetBy: r.upperBound)
        return self.substring(with: Range<String.Index>(uncheckedBounds: (lower: fromIndex, upper: toIndex)))
    }

    func character(_ at: Int) -> Character {
        return self[self.index(self.startIndex, offsetBy: at)]
    }

    func lastIndexOfCharacter(_ c: Character) -> Int? {
        return range(of: String(c), options: .backwards)?.lowerBound.encodedOffset
    }
}
Run Code Online (Sandbox Code Playgroud)

Swift 4的更新扩展

public extension String {

    //right is the first encountered string after left
    func between(_ left: String, _ right: String) -> String? {
        guard
            let leftRange = range(of: left), let rightRange = range(of: right, options: .backwards)
            , leftRange.upperBound <= rightRange.lowerBound
            else { return nil }

        let sub = self[leftRange.upperBound...]
        let closestToLeftRange = sub.range(of: right)!            
        return String(sub[..<closestToLeftRange.lowerBound])
    }

    var length: Int {
        get {
            return self.count
        }
    }

    func substring(to : Int) -> String {
        let toIndex = self.index(self.startIndex, offsetBy: to)
        return String(self[...toIndex])
    }

    func substring(from : Int) -> String {
        let fromIndex = self.index(self.startIndex, offsetBy: from)
        return String(self[fromIndex...])
    }

    func substring(_ r: Range<Int>) -> String {
        let fromIndex = self.index(self.startIndex, offsetBy: r.lowerBound)
        let toIndex = self.index(self.startIndex, offsetBy: r.upperBound)
        let indexRange = Range<String.Index>(uncheckedBounds: (lower: fromIndex, upper: toIndex))
        return String(self[indexRange])
    }

    func character(_ at: Int) -> Character {
        return self[self.index(self.startIndex, offsetBy: at)]
    }

    func lastIndexOfCharacter(_ c: Character) -> Int? {
        return range(of: String(c), options: .backwards)?.lowerBound.encodedOffset
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

let text = "www.stackoverflow.com"
let at = text.character(3) // .
let range = text.substring(0..<3) // www
let from = text.substring(from: 4) // stackoverflow.com
let to = text.substring(to: 16) // www.stackoverflow
let between = text.between(".", ".") // stackoverflow
let substringToLastIndexOfChar = text.lastIndexOfCharacter(".") // 17
Run Code Online (Sandbox Code Playgroud)

PS真的很奇怪,开发商不得不处理String.Index而不是简单Int.我们为什么要打扰内部String机制,而不仅仅是简单的substring()方法?


Mar*_*rný 14

我会使用下标(s[start..<end]):

斯威夫特2

let s = "www.stackoverflow.com"
let start = s.startIndex
let end = s.index(s.endIndex, offsetBy: -4)
let substring = s[start..<end] // www.stackoverflow
Run Code Online (Sandbox Code Playgroud)

斯威夫特3

let s = "www.stackoverflow.com"
let start = s.startIndex
let end = s.index(s.endIndex, offsetBy: -4)
let substring = s[start..<end] // www.stackoverflow
Run Code Online (Sandbox Code Playgroud)


Leo*_*bus 8

例如,将"www.stackoverflow.com"转换为"www.stackoverflow".什么代码片段可以做到这一点,并且是最快速的?

可以更像Swift吗?

let string = "www.stackoverflow.com".stringByDeletingPathExtension   // "www.stackoverflow"
Run Code Online (Sandbox Code Playgroud)

更新:Xcode 7.2.1•Swift 2.1.1

extension String {
    var nsValue: NSString {
        return self
    }
}

let string = "www.stackoverflow.com".nsValue.stringByDeletingPathExtension   // "www.stackoverflow"
Run Code Online (Sandbox Code Playgroud)

编辑/更新:

在Swift 4或更高版本(Xcode 10.0+)中,您可以使用新的Array lastIndex(of :)

func lastIndex(of element: Element) -> Int?
Run Code Online (Sandbox Code Playgroud)
let string = "www.stackoverflow.com"
if let lastIndex = string.lastIndex(of: ".") {
    let subString = string[..<lastIndex]  // "www.stackoverflow"
}
Run Code Online (Sandbox Code Playgroud)


joe*_*son 7

这是我如何做到的.您可以以相同的方式执行此操作,也可以将此代码用于创意.

let s = "www.stackoverflow.com"
s.substringWithRange(0..<s.lastIndexOf("."))
Run Code Online (Sandbox Code Playgroud)

以下是我使用的扩展:

import Foundation
extension String {

  var length: Int {
    get {
      return countElements(self)
    }
  }

  func indexOf(target: String) -> Int {
    var range = self.rangeOfString(target)
    if let range = range {
      return distance(self.startIndex, range.startIndex)
    } else {
      return -1
    }
  }

  func indexOf(target: String, startIndex: Int) -> Int {
    var startRange = advance(self.startIndex, startIndex)        
    var range = self.rangeOfString(target, options: NSStringCompareOptions.LiteralSearch, range: Range<String.Index>(start: startRange, end: self.endIndex))
    if let range = range {
      return distance(self.startIndex, range.startIndex)
    } else {
      return -1
    }
  }

  func lastIndexOf(target: String) -> Int {
    var index = -1
    var stepIndex = self.indexOf(target)
    while stepIndex > -1 {
      index = stepIndex
      if stepIndex + target.length < self.length {
        stepIndex = indexOf(target, startIndex: stepIndex + target.length)
      } else {
        stepIndex = -1
      }
    }
    return 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)
  }

}
Run Code Online (Sandbox Code Playgroud)

信用albertbori/Common Swift字符串扩展

一般来说,我是扩展的强烈支持者,特别是对于字符串操作,搜索和切片等需求.

  • 我认为这些都是Apple应该做的事情,以改进Swift中String的API. (2认同)
  • 寻找这个API.Apple Swift缺少很多基本API.这是一种非常复杂的语言,也不完整.斯威夫特是胡说八道! (2认同)

rin*_*aro 6

String 具有内置子字符串功能:

extension String : Sliceable {
    subscript (subRange: Range<String.Index>) -> String { get }
}
Run Code Online (Sandbox Code Playgroud)

如果您想要的是“转到字符的第一个索引”,则可以使用内置find()函数获取子字符串:

var str = "www.stackexchange.com"
str[str.startIndex ..< find(str, ".")!] // -> "www"
Run Code Online (Sandbox Code Playgroud)

要找到最后一个索引,我们可以实现findLast().

/// Returns the last index where `value` appears in `domain` or `nil` if
/// `value` is not found.
///
/// Complexity: O(\ `countElements(domain)`\ )
func findLast<C: CollectionType where C.Generator.Element: Equatable>(domain: C, value: C.Generator.Element) -> C.Index? {
    var last:C.Index? = nil
    for i in domain.startIndex..<domain.endIndex {
        if domain[i] == value {
            last = i
        }
    }
    return last
}

let str = "www.stackexchange.com"
let substring = map(findLast(str, ".")) { str[str.startIndex ..< $0] } // as String?
// if "." is found, substring has some, otherwise `nil`
Run Code Online (Sandbox Code Playgroud)

添加:

也许,BidirectionalIndexType专业版findLast更快:

func findLast<C: CollectionType where C.Generator.Element: Equatable, C.Index: BidirectionalIndexType>(domain: C, value: C.Generator.Element) -> C.Index? {
    for i in lazy(domain.startIndex ..< domain.endIndex).reverse() {
        if domain[i] == value {
            return i
        }
    }
    return nil
}
Run Code Online (Sandbox Code Playgroud)


Chi*_*buZ 6

您可以使用这些扩展:

斯威夫特 2.3

 extension String
    {
        func substringFromIndex(index: Int) -> String
        {
            if (index < 0 || index > self.characters.count)
            {
                print("index \(index) out of bounds")
                return ""
            }
            return self.substringFromIndex(self.startIndex.advancedBy(index))
        }

        func substringToIndex(index: Int) -> String
        {
            if (index < 0 || index > self.characters.count)
            {
                print("index \(index) out of bounds")
                return ""
            }
            return self.substringToIndex(self.startIndex.advancedBy(index))
        }

        func substringWithRange(start: Int, end: Int) -> String
        {
            if (start < 0 || start > self.characters.count)
            {
                print("start index \(start) out of bounds")
                return ""
            }
            else if end < 0 || end > self.characters.count
            {
                print("end index \(end) out of bounds")
                return ""
            }
            let range = Range(start: self.startIndex.advancedBy(start), end: self.startIndex.advancedBy(end))
            return self.substringWithRange(range)
        }

        func substringWithRange(start: Int, location: Int) -> String
        {
            if (start < 0 || start > self.characters.count)
            {
                print("start index \(start) out of bounds")
                return ""
            }
            else if location < 0 || start + location > self.characters.count
            {
                print("end index \(start + location) out of bounds")
                return ""
            }
            let range = Range(start: self.startIndex.advancedBy(start), end: self.startIndex.advancedBy(start + location))
            return self.substringWithRange(range)
        }
    }
Run Code Online (Sandbox Code Playgroud)

斯威夫特 3

extension String
{   
    func substring(from index: Int) -> String
    {
        if (index < 0 || index > self.characters.count)
        {
            print("index \(index) out of bounds")
            return ""
        }
        return self.substring(from: self.characters.index(self.startIndex, offsetBy: index))
    }

    func substring(to index: Int) -> String
    {
        if (index < 0 || index > self.characters.count)
        {
            print("index \(index) out of bounds")
            return ""
        }
        return self.substring(to: self.characters.index(self.startIndex, offsetBy: index))
    }

    func substring(start: Int, end: Int) -> String
    {
        if (start < 0 || start > self.characters.count)
        {
            print("start index \(start) out of bounds")
            return ""
        }
        else if end < 0 || end > self.characters.count
        {
            print("end index \(end) out of bounds")
            return ""
        }
        let startIndex = self.characters.index(self.startIndex, offsetBy: start)
        let endIndex = self.characters.index(self.startIndex, offsetBy: end)
        let range = startIndex..<endIndex

        return self.substring(with: range)
    }

    func substring(start: Int, location: Int) -> String
    {
        if (start < 0 || start > self.characters.count)
        {
            print("start index \(start) out of bounds")
            return ""
        }
        else if location < 0 || start + location > self.characters.count
        {
            print("end index \(start + location) out of bounds")
            return ""
        }
        let startIndex = self.characters.index(self.startIndex, offsetBy: start)
        let endIndex = self.characters.index(self.startIndex, offsetBy: start + location)
        let range = startIndex..<endIndex

        return self.substring(with: range)
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

let string = "www.stackoverflow.com"        
let substring = string.substringToIndex(string.characters.count-4)
Run Code Online (Sandbox Code Playgroud)


Ale*_*kov 6

斯威夫特 4:

extension String {

    /// the length of the string
    var length: Int {
        return self.characters.count
    }

    /// Get substring, e.g. "ABCDE".substring(index: 2, length: 3) -> "CDE"
    ///
    /// - parameter index:  the start index
    /// - parameter length: the length of the substring
    ///
    /// - returns: the substring
    public func substring(index: Int, length: Int) -> String {
        if self.length <= index {
            return ""
        }
        let leftIndex = self.index(self.startIndex, offsetBy: index)
        if self.length <= index + length {
            return self.substring(from: leftIndex)
        }
        let rightIndex = self.index(self.endIndex, offsetBy: -(self.length - index - length))
        return self.substring(with: leftIndex..<rightIndex)
    }

    /// Get substring, e.g. -> "ABCDE".substring(left: 0, right: 2) -> "ABC"
    ///
    /// - parameter left:  the start index
    /// - parameter right: the end index
    ///
    /// - returns: the substring
    public func substring(left: Int, right: Int) -> String {
        if length <= left {
            return ""
        }
        let leftIndex = self.index(self.startIndex, offsetBy: left)
        if length <= right {
            return self.substring(from: leftIndex)
        }
        else {
            let rightIndex = self.index(self.endIndex, offsetBy: -self.length + right + 1)
            return self.substring(with: leftIndex..<rightIndex)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以按如下方式进行测试:

    print("test: " + String("ABCDE".substring(index: 2, length: 3) == "CDE"))
    print("test: " + String("ABCDE".substring(index: 0, length: 3) == "ABC"))
    print("test: " + String("ABCDE".substring(index: 2, length: 1000) == "CDE"))
    print("test: " + String("ABCDE".substring(left: 0, right: 2) == "ABC"))
    print("test: " + String("ABCDE".substring(left: 1, right: 3) == "BCD"))
    print("test: " + String("ABCDE".substring(left: 3, right: 1000) == "DE"))
Run Code Online (Sandbox Code Playgroud)

检查https://gitlab.com/seriyvolk83/SwiftEx库。它包含这些和其他有用的方法。


bau*_*sic 6

增加咔嗒声的一件事是重复 stringVar:

stringVar [ stringVar .index( stringVar .startIndex, offsetBy: ...)

在斯威夫特 4

扩展可以减少其中的一些:

extension String {

    func index(at location: Int) -> String.Index {
        return self.index(self.startIndex, offsetBy: location)
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,用法:

let string = "abcde"

let to = string[..<string.index(at: 3)] // abc
let from = string[string.index(at: 3)...] // de
Run Code Online (Sandbox Code Playgroud)

需要注意的是to和from是类型Substring(或String.SubSequance)。它们不分配新字符串并且处理效率更高。

要取回String类型,Substring需要将其强制转换为String:

let backToString = String(from)
Run Code Online (Sandbox Code Playgroud)

这是最终分配字符串的地方。


Ima*_*tit 5

你想得到一个字符串的子串,从起始索引到它的一个字符的最后一个索引吗?如果是这样,您可以选择以下 Swift 2.0+ 方法之一。

需要的方法 Foundation

获取包含字符的最后一个索引的子字符串:

import Foundation

let string = "www.stackoverflow.com"
if let rangeOfIndex = string.rangeOfCharacterFromSet(NSCharacterSet(charactersInString: "."), options: .BackwardsSearch) {
    print(string.substringToIndex(rangeOfIndex.endIndex))
}

// prints "www.stackoverflow."
Run Code Online (Sandbox Code Playgroud)

获取不包含字符的最后一个索引的子字符串:

import Foundation

let string = "www.stackoverflow.com"
if let rangeOfIndex = string.rangeOfCharacterFromSet(NSCharacterSet(charactersInString: "."), options: .BackwardsSearch) {
    print(string.substringToIndex(rangeOfIndex.startIndex))
}

// prints "www.stackoverflow"
Run Code Online (Sandbox Code Playgroud)

如果您需要重复这些操作,扩展String可能是一个很好的解决方案:

import Foundation

extension String {
    func substringWithLastInstanceOf(character: Character) -> String? {
        if let rangeOfIndex = rangeOfCharacterFromSet(NSCharacterSet(charactersInString: String(character)), options: .BackwardsSearch) {
            return self.substringToIndex(rangeOfIndex.endIndex)
        }
        return nil
    }
    func substringWithoutLastInstanceOf(character: Character) -> String? {
        if let rangeOfIndex = rangeOfCharacterFromSet(NSCharacterSet(charactersInString: String(character)), options: .BackwardsSearch) {
            return self.substringToIndex(rangeOfIndex.startIndex)
        }
        return nil
    }
}

print("www.stackoverflow.com".substringWithLastInstanceOf("."))
print("www.stackoverflow.com".substringWithoutLastInstanceOf("."))

/*
prints:
Optional("www.stackoverflow.")
Optional("www.stackoverflow")
*/
Run Code Online (Sandbox Code Playgroud)

不需要的方法 Foundation

获取包含字符的最后一个索引的子字符串:

let string = "www.stackoverflow.com"
if let reverseIndex = string.characters.reverse().indexOf(".") {
    print(string[string.startIndex ..< reverseIndex.base])
}

// prints "www.stackoverflow."
Run Code Online (Sandbox Code Playgroud)

获取不包含字符的最后一个索引的子字符串:

let string = "www.stackoverflow.com"
if let reverseIndex = string.characters.reverse().indexOf(".") {
    print(string[string.startIndex ..< reverseIndex.base.advancedBy(-1)])
}

// prints "www.stackoverflow"
Run Code Online (Sandbox Code Playgroud)

如果您需要重复这些操作,扩展String可能是一个很好的解决方案:

extension String {
    func substringWithLastInstanceOf(character: Character) -> String? {
        if let reverseIndex = characters.reverse().indexOf(".") {
            return self[self.startIndex ..< reverseIndex.base]
        }
        return nil
    }
    func substringWithoutLastInstanceOf(character: Character) -> String? {
        if let reverseIndex = characters.reverse().indexOf(".") {
            return self[self.startIndex ..< reverseIndex.base.advancedBy(-1)]
        }
        return nil
    }
}

print("www.stackoverflow.com".substringWithLastInstanceOf("."))
print("www.stackoverflow.com".substringWithoutLastInstanceOf("."))

/*
prints:
Optional("www.stackoverflow.")
Optional("www.stackoverflow")
*/
Run Code Online (Sandbox Code Playgroud)


And*_*eev 5

如果您知道索引,这里有一个简单而简短的方法来获取子字符串:

let s = "www.stackoverflow.com"
let result = String(s.characters.prefix(17)) // "www.stackoverflow"
Run Code Online (Sandbox Code Playgroud)

如果您的索引超过字符串的长度,它不会使应用程序崩溃:

let s = "short"
let result = String(s.characters.prefix(17)) // "short"
Run Code Online (Sandbox Code Playgroud)

这两个示例都是Swift 3 ready。