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

前端 未结 22 734
感情败类
感情败类 2020-11-30 19:09

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.

相关标签:
22条回答
  • 2020-11-30 20:05

    String has builtin substring feature:

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

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

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

    To find last index, we can implement 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`
    

    ADDED:

    Maybe, BidirectionalIndexType specialized version of findLast is faster:

    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
    }
    
    0 讨论(0)
  • 2020-11-30 20:06

    The one thing that adds clatter is the repeated stringVar:

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

    In Swift 4

    An extension can reduce some of that:

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

    Then, usage:

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

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

    To get back a String type, Substring needs to be casted back to String:

    let backToString = String(from)
    

    This is where a string is finally allocated.

    0 讨论(0)
  • 2020-11-30 20:06

    I also build a simple String-extension for Swift 4:

    extension String {
        func subStr(s: Int, l: Int) -> String { //s=start, l=lenth
            let r = Range(NSRange(location: s, length: l))!
            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])
         }
    }
    

    So you can easily call it like this:

    "Hallo world".subStr(s: 1, l: 3) //prints --> "all"
    
    0 讨论(0)
  • 2020-11-30 20:06

    Try this Int-based workaround:

    extension String {
        // start and end is included
        func intBasedSubstring(_ start: Int, _ end: Int) -> String {
            let endOffset: Int = -(count - end - 1)
            let startIdx = self.index(startIndex, offsetBy: start)
            let endIdx = self.index(endIndex, offsetBy: endOffset)
            return String(self[startIdx..<endIdx])
        }
    }
    

    Note: It's just a practice. It doesn't check the boundary. Modify to suit your needs.

    0 讨论(0)
提交回复
热议问题