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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28182441/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 04:33:04  来源:igfitidea点击:

Swift: How to get substring from start to last index of character

iosswift

提问by Jason Hocker

I want to learn the best/simplest way to turn a string into another string but with only a subset, starting at the beginning and going to the last index of a character.

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

For example, convert "www.stackoverflow.com" to "www.stackoverflow". What code snippet would do that, and being the most swift-like? (I hope this doesn't bring a debate, but I can't find good lesson on how to handle substrings in Swift.

例如,将“www.stackoverflow.com”转换为“www.stackoverflow”。什么代码片段会做到这一点,并且是最快速的?(我希望这不会引起争论,但我找不到关于如何在 Swift 中处理子字符串的好课程。

回答by fpg1503

Just accessing backward

只是向后访问

The best way is to use substringToIndexcombined to the endIndexproperty and the advanceglobal function.

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

var string1 = "www.stackoverflow.com"

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

var substring1 = string1.substringToIndex(index1)

Looking for a string starting from the back

寻找从后面开始的字符串

Use rangeOfStringand set optionsto .BackwardsSearch

使用rangeOfString并设置options.BackwardsSearch

var string2 = "www.stackoverflow.com"

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

var substring2 = string2.substringToIndex(index2!)

No extensions, pure idiomatic Swift

没有扩展,纯惯用 Swift

Swift 2.0

斯威夫特 2.0

advanceis now a part of Indexand is called advancedBy. You do it like:

advance现在是 的一部分Index并被称为advancedBy。你这样做:

var string1 = "www.stackoverflow.com"

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

var substring1 = string1.substringToIndex(index1)

Swift 3.0

斯威夫特 3.0

You can't call advancedByon a Stringbecause it has variable size elements. You have to use index(_, offsetBy:).

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

var string1 = "www.stackoverflow.com"

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

var substring1 = string1.substring(to: index1)

A lot of things have been renamed. The cases are written in camelCase, startIndexbecame lowerBound.

很多东西都改名了。这些案例是用驼峰式写成的,startIndex变成了lowerBound.

var string2 = "www.stackoverflow.com"

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

var substring2 = string2.substring(to: index2!)

Also, I wouldn't recommend force unwrapping index2. You can use optional binding or map. Personally, I prefer using map:

另外,我不建议 force unwrapping index2。您可以使用可选绑定或map. 就个人而言,我更喜欢使用map

var substring3 = index2.map(string2.substring(to:))

Swift 4

斯威夫特 4

The Swift 3 version is still valid but now you can now use subscripts with indexes ranges:

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

let string1 = "www.stackoverflow.com"

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

let substring1 = string1[..<index1]

The second approach remains unchanged:

第二种方法保持不变:

let string2 = "www.stackoverflow.com"

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

let substring3 = index2.map(string2.substring(to:))

回答by serg_zhd

Swift 3, XCode 8

斯威夫特 3,XCode 8

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

Since advancedBy(Int)is gone since Swift 3 use String's method index(String.Index, Int). Check out this Stringextension with substring and friends:

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

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? {
        guard let index = range(of: String(c), options: .backwards)?.lowerBound else
        { return nil }
        return distance(from: startIndex, to: index)
    }
}


UPDATED extension for Swift 4

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? {
        guard let index = range(of: String(c), options: .backwards)?.lowerBound else
        { return nil }
        return distance(from: startIndex, to: index)
    }
}

Usage:

用法:

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

P.S. It's really odd that developers forced to deal with String.Indexinstead of plain Int. Why should we bother about internal Stringmechanics and not just have simple substring()methods?

PS 开发人员被迫处理String.Index而不是普通Int. 为什么我们要关心内部String机制而不是简单的substring()方法?

回答by Marián ?erny

I would do it using a subscript (s[start..<end]):

我会使用下标 ( s[start..<end])来做到这一点:

Swift 3, 4, 5

斯威夫特 3、4、5

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

回答by Leo Dabus

edit/update:

编辑/更新:

In Swift 4 or later(Xcode 10.0+) you can use the new BidirectionalCollectionmethod lastIndex(of:)

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

func lastIndex(of element: Element) -> Int?


let string = "www.stackoverflow.com"
if let lastIndex = string.lastIndex(of: ".") {
    let subString = string[..<lastIndex]  // "www.stackoverflow"
}

回答by joelparkerhenderson

Here's how I do it. You could do it the same way, or use this code for ideas.

这是我如何做到的。你可以用同样的方式来做,或者使用这个代码来获得想法。

let s = "www.stackoverflow.com"
s.substringWithRange(0..<s.lastIndexOf("."))

Here are the extensions I use:

以下是我使用的扩展:

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

}

Credit albertbori / Common Swift String Extensions

信用albertbori / 通用 Swift 字符串扩展

Generally I am a strong proponent of extensions, especially for needs like string manipulation, searching, and slicing.

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

回答by rintaro

Stringhas builtin substring feature:

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

extension String : Sliceable {
    subscript (subRange: Range<String.Index>) -> String { get }
}

If what you want is "going to the firstindex of a character", you can get the substring using builtin find()function:

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

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

To find lastindex, we can implement findLast().

要找到最后一个索引,我们可以实现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 ..< 
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
}
] } // as String? // if "." is found, substring has some, otherwise `nil`


ADDED:

添加:

Maybe, BidirectionalIndexTypespecialized version of findLastis faster:

也许,BidirectionalIndexType专业版findLast更快:

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

回答by ChikabuZ

You can use these extensions:

您可以使用这些扩展:

Swift 2.3

斯威夫特 2.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)
    }
}

Swift 3

斯威夫特 3

let string = "www.stackoverflow.com"        
let substring = string.substringToIndex(string.characters.count-4)

Usage:

用法:

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

回答by Alexander Volkov

Swift 4:

斯威夫特 4:

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

you can test it as follows:

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

import Foundation

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

// prints "www.stackoverflow."

Check https://gitlab.com/seriyvolk83/SwiftExlibrary. It contains these and other helpful methods.

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

回答by Imanou Petit

Do you want to get a substring of a string from start index to the last index of one of its characters? If so, you may choose one of the following Swift 2.0+ methods.

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

Methods that require Foundation

需要的方法 Foundation

Get a substring that includes the last index of a character:

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

import Foundation

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

// prints "www.stackoverflow"

Get a substring that DOES NOT include the last index of a character:

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

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")
*/


If you need to repeat those operations, extending Stringcan be a good solution:

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

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

// prints "www.stackoverflow."


Methods that DO NOT require Foundation

不需要的方法 Foundation

Get a substring that includes the last index of a character:

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

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

// prints "www.stackoverflow"

Get a substring that DOES NOT include the last index of a character:

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

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")
*/


If you need to repeat those operations, extending Stringcan be a good solution:

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

extension String {

    func index(at: Int) -> String.Index {
        return self.index(self.startIndex, offsetBy: at)
    }
}

回答by bauerMusic

The one thing that adds clatter is the repeated stringVar:

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

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

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

In Swift 4

斯威夫特 4

An extension can reduce some of that:

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

let string = "abcde"

let to = string[..<string.index(at: 3)] // abc
let from = string[string.index(at: 3)...] // de

Then, usage:

然后,用法:

let backToString = String(from)

It should be noted that toand fromare type Substring(or String.SubSequance). They do not allocate new strings and are more efficient for processing.

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

To get back a Stringtype, Substringneeds to be casted back to String:

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

##代码##

This is where a string is finally allocated.

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