Swift Get string between 2 strings in a string

36,135

Solution 1

I'd use a regular expression to extract substrings from complex input like this.

Swift 3.1:

let test = "javascript:getInfo(1,'Info/99/something', 'City Hall',1, 99);"

if let match = test.range(of: "(?<=')[^']+", options: .regularExpression) {
    print(test.substring(with: match))
}

// Prints: Info/99/something

Swift 2.0:

let test = "javascript:getInfo(1,'Info/99/something', 'City Hall',1, 99);"

if let match = test.rangeOfString("(?<=')[^']+", options: .RegularExpressionSearch) {
    print(test.substringWithRange(match))
}

// Prints: Info/99/something

Solution 2

extension String {
    
    func slice(from: String, to: String) -> String? {
        return (range(of: from)?.upperBound).flatMap { substringFrom in
            (range(of: to, range: substringFrom..<endIndex)?.lowerBound).map { substringTo in
                String(self[substringFrom..<substringTo])
            }
        }
    }
}

"javascript:getInfo(1,'Info/99/something', 'City Hall',1, 99);"
  .sliceFrom("'", to: "',")

Solution 3

In Swift 4 or later you can create an extension method on StringProtocol to support substrings as well. You can just return a Substring instead of a new String:

edit/update: Swift 5 or later

extension StringProtocol  {
    func substring<S: StringProtocol>(from start: S, options: String.CompareOptions = []) -> SubSequence? {
        guard let lower = range(of: start, options: options)?.upperBound
        else { return nil }
        return self[lower...]
    }
    func substring<S: StringProtocol>(through end: S, options: String.CompareOptions = []) -> SubSequence? {
        guard let upper = range(of: end, options: options)?.upperBound
        else { return nil }
        return self[..<upper]
    }
    func substring<S: StringProtocol>(upTo end: S, options: String.CompareOptions = []) -> SubSequence? {
        guard let upper = range(of: end, options: options)?.lowerBound
        else { return nil }
        return self[..<upper]
    }
    func substring<S: StringProtocol, T: StringProtocol>(from start: S, upTo end: T, options: String.CompareOptions = []) -> SubSequence? {
        guard let lower = range(of: start, options: options)?.upperBound,
            let upper = self[lower...].range(of: end, options: options)?.lowerBound
        else { return nil }
        return self[lower..<upper]
    }
    func substring<S: StringProtocol, T: StringProtocol>(from start: S, through end: T, options: String.CompareOptions = []) -> SubSequence? {
        guard let lower = range(of: start, options: options)?.upperBound,
            let upper = self[lower...].range(of: end, options: options)?.upperBound
        else { return nil }
        return self[lower..<upper]
    }
}

Usage:

let string = "javascript:getInfo(1,'Info/99/something', 'City Hall',1, 99);"
let substr = string.substring(from: "'")                   // "Info/99/something', 'City Hall',1, 99);"
let through = string.substring(through: "Info")  // "javascript:getInfo"
let upTo = string.substring(upTo: "Info")  // "javascript:get"
let fromUpTo = string.substring(from: "'", upTo: "',")  // "Info/99/something"
let fromThrough = string.substring(from: "'", through: "',")  // "Info/99/something',"

let fromUpToCaseInsensitive = string.substring(from: "'info/", upTo: "/something", options: .caseInsensitive)  // "99"

Solution 4

Consider using a regular expression to match everything between single quotes.

let string = "javascript:getInfo(1,'Info/99/something', 'City Hall',1, 99);"

let pattern = "'(.+?)'"
let regex = NSRegularExpression(pattern: pattern, options: nil, error: nil)
let results = regex!.matchesInString(string, options: nil, range: NSMakeRange(0, count(string)))  as! [NSTextCheckingResult]

let nsstring = string as NSString
let matches = results.map { result in return nsstring.substringWithRange(result.range)}

// First match
println(matches[0])
Share:
36,135
Alp
Author by

Alp

I am an enthusiastic game developer and game designer. I mostly work on unity3d engine and a little bit on unreal engine. I like dragons,wolves,horses. I have many hobbies; anime ,reading, running. fighting, wing tsun ,kickbox , ninjutsu, story writing and video games.

Updated on July 09, 2022

Comments

  • Alp
    Alp almost 2 years

    I am getting a string from html parse that is;

    string = "javascript:getInfo(1,'Info/99/something', 'City Hall',1, 99);"
    

    my code is something like

    var startIndex = text.rangeOfString("'")
    var endIndex = text.rangeOfString("',")
    var range2 = startIndex2...endIndex
    substr= string.substringWithRange(range)
    

    i am not sure if my second splitting string should be "'" or "',"

    i want my outcome as

    substr = "Info/99/something"
    
  • CodyMace
    CodyMace over 7 years
    This is awesome! Thanks!
  • Makaille
    Makaille over 7 years
    @oisdk does it work if we need to slice from a string to the end of the string ? Ex : "blabla popo titi toto" -> slice(from "popo", to : endOfString) ?
  • Chameleon
    Chameleon about 6 years
    amazing and simple
  • Martin R
    Martin R about 6 years
    In Swift 4 you can also compute the second range as self[substringFrom...].range(of: to), that saves some keystrokes and the mention of endIndex.
  • pkamb
    pkamb over 4 years
    I found this return flatmap.().map() code pretty hard to grok for the function; so I tried rewriting a version using guard.
  • bshirley
    bshirley over 4 years
    For more detail on the regex, see stackoverflow.com/questions/6109882/…
  • Mendy
    Mendy almost 3 years
    @Makaille see this answer