-1

I found this code online

let sample = "#This is an [hello] amazing [world]"
let regex = try? NSRegularExpression(pattern: "#(.*?)\\[.+?\\]", options: [])
let matches = regex!.matches(in: sample, options: [], range: NSMakeRange(0, sample.count))
for match in matches {
    let r = (sample as NSString).substring(with: match.range)
    print(r)
}

Current output

#This is an [hello]

expected output

hello

How can I extract only the needed text?

NOTE

In my case there could be multiple square brackets. See "[anotherWorld] #This is an [hello] amazing [world]". I need the text between the square brackets which is immediately next to #somerandomtext. In given sample I need hello only

iOSDev
  • 326
  • 2
  • 10
  • Your regex is correct. Just a little amendment needed. Check [**this.**](https://regex101.com/r/WWx7Mp/2) Does it help? –  Jun 13 '20 at 08:09
  • @Mandy8055 How to use the `$1` thing in swift? – iOSDev Jun 13 '20 at 08:11
  • Perhaps [**rangeAt(1)**](https://developer.apple.com/reference/foundation/nstextcheckingresult/1416732-rangeat). Check the working example in this answer. Does it help? –  Jun 13 '20 at 08:12
  • @Mandy8055. Thanks it works. What is `$0` and `$1`? – iOSDev Jun 13 '20 at 08:20
  • 1
    It is the captured group numbers. In javascript we use `$` for captured groups. In many languages we use ```\\``` for captured groups, etc. In swift we use `rangeAt(group number)` function. –  Jun 13 '20 at 08:22

3 Answers3

2

You have to use a capture group to get the text between specific characters.

The pattern

"#[^\[]+\[([^\]]+)\]"

searches for a # then for one or more characters which are not [ ([^\[]+)then a [ (\[) then it captures () one or more characters which are not ] ([^\]]+) and finally it searches for a ] (\]).

let sample = "#This is an [hello] amazing [world]"
let regex = try! NSRegularExpression(pattern:  #"#[^\[]+\[([^\]]+)\]"#)
if let match = regex.firstMatch(in: sample, range: NSRange(sample.startIndex..., in: sample)) {
    let range = Range(match.range(at: 1), in: sample)!
    let result = String(sample[range])
    print(result)
}

Your way to create an NSRange and the bridge to NSString are bad practices. There are dedicated APIs to handle NSRange

vadian
  • 274,689
  • 30
  • 353
  • 361
  • Thanks. Almost correct. I want `hello` only. I dont need 'world'. `This is an` is an random text. – iOSDev Jun 13 '20 at 08:08
  • String between `#` and `[` is random. – iOSDev Jun 13 '20 at 08:09
  • Please see the edit. The beginning of the string is irrelevant. – vadian Jun 13 '20 at 08:09
  • @vadian you can avoid using backslash on your regex patterns using `#"whatever"#` in this case `#"\[([^\]]+)\]"#` – Leo Dabus Jun 13 '20 at 08:10
  • In my case there could be multiple square brackets. See `"[anotherWorld] #This is an [hello] amazing [world]"`. I need the text between the square brackets which is immediately next to `#somerandomtext`. In given sample I need `hello` only – iOSDev Jun 13 '20 at 08:16
0

If there should not be a # or [ or ] after matching the # and the wanted match from [...], you could make the pattern a bit more restrictive extending the negated character class and use a capturing group:

#[^#\]\[]+\[([^\]\[]+)\]

In parts

  • # Match a # char
  • [^#\]\[]+ Negated character class, match 1+ times any char except # [ ]
  • \[ Match [
    • ( Capture group 1
    • [^\]\[]+ match 1+ times any char except [ ]
  • ) Close group
  • \] Match ]

Regex demo

The fourth bird
  • 154,723
  • 16
  • 55
  • 70
0

The regex approach it is already answered so I will post a native Swift approach:

let string = "[anotherWorld] #This is an [hello] amazing [world]"
if let hashTag = string.firstIndex(of: "#"),
    let openBracket = string[hashTag...].firstIndex(of: "["),
    let closedBracket = string[openBracket...].firstIndex(of: "]") {
    let substring = string[string.index(after: openBracket)..<closedBracket]
    let result = String(substring)
    print(result)  // "hello\n"
}

String manipulation in Swift is not very Swifty but you can extend Collection and create some SubSequence methods to make your life easier:

extension Collection where Element: Equatable {

    func firstIndex(after element: Element) -> Index? {
        guard let index = firstIndex(of: element) else { return nil }
        return self.index(after: index)
    }

    func subSequence(from element: Element) -> SubSequence? {
        guard let index = firstIndex(of: element) else { return nil }
        return self[index...]
    }
    func subSequence(after element: Element) -> SubSequence? {
        guard let index = firstIndex(after: element) else { return nil }
        return self[index...]
    }

    func subSequence(upTo element: Element) -> SubSequence? {
        guard let index = firstIndex(after: element) else { return nil }
        return self[..<index]
    }
    func subSequence(upThrough element: Element) -> SubSequence? {
        guard let index = firstIndex(of: element) else { return nil }
        return self[...index]
    }

    func subSequence(from element: Element, upTo: Element) -> SubSequence? {
        guard
            let lower = firstIndex(of: element),
            let upper = self[lower...].firstIndex(of: upTo)
        else { return nil }
        return self[lower..<upper]
    }
    func subSequence(from element: Element, upThrough: Element) -> SubSequence? {
        guard
            let lower = firstIndex(of: element),
            let upper = self[lower...].firstIndex(of: upThrough)
        else { return nil }
        return self[lower...upper]
    }

    func subSequence(after element: Element, upTo: Element) -> SubSequence? {
        guard
            let lower = firstIndex(after: element),
            let upper = self[lower...].firstIndex(of: upTo)
        else { return nil }
        return self[lower..<upper]
    }
    func subSequence(after element: Element, upThrough: Element) -> SubSequence? {
        guard
            let lower = firstIndex(after: element),
            let upper = self[lower...].firstIndex(of: upThrough)
        else { return nil }
        return self[lower...upper]
    }
}

Now you can get any subsequence between two elements (in the String case a Substring between two characters):

let string = "[anotherWorld] #This is an [hello] amazing [world]"
let subString = string.subSequence(after: "#")?.subSequence(after: "[", upTo: "]")  // "hello"
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571