-2

I'm very, very new to Swift and admittedly struggling with some of its constructs. I have to work with a text file and do many manipulations - here's an example to illustrate the point:

let's say I have a text like this (multi line)

   Mary had a little lamb

   @name: a name

   @summary: a paragraph of text 

   {{something}}

   a whole bunch of multi-line text

  x----------------x

I want to be able to do simple things like find the location of @name, then split it to get the name and so on. I've done this in javascript and it was pretty simple with the use of substr and the regex matches.

In swift, which is supposed to be swift and easy and what not, I'm finding this exceedingly confusing.

Can someone help with how one might do

  1. Find the location of the start of a substring

  2. Extract all text between from the end of a substring to the end of text

Sorry if this is trivial - but the Apple documentation feels very complicated, and lots of examples are years old. I can't also seem to find easy application of regex.

Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
Gerry
  • 866
  • 2
  • 12
  • 31

2 Answers2

1

You can do this with range() and distance():

let str = "Example string"
let range = str.range(of: "amp")!
print(str.distance(from: str.startIndex, to: range.lowerBound)) // 2
let lastStr = str[range.upperBound...]
print(lastStr) // "le string"
Xcoder
  • 1,433
  • 3
  • 17
  • 37
1

You can use string range(of: String) method to find the range of your string, get its upperBound and search for the end of the line from that position of the string:


Playground testing:

let sentence = """
Mary had a little lamb

@name: a name

@summary: a paragraph of text

{{something}}

a whole bunch of multi-line text
"""

if let start = sentence.range(of: "@name:")?.upperBound,
    let end = sentence[start...].range(of: "\n")?.lowerBound {
    let substring = sentence[start..<end]
    print("name:", substring)
}

If you need to get the string from there to the end of the string you can use PartialRangeFrom:

if let start = sentence.range(of: "@summary:")?.upperBound {
    let substring = sentence[start...]
    print("summary:", substring)
}

If you find yourself using that a lot you can extend StringProtocol and create your own method:

 extension StringProtocol {
    func substring<S:StringProtocol,T:StringProtocol>(between start: S, and 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>(after string: S, options: String.CompareOptions = []) -> SubSequence? {
        guard
            let lower = range(of: string, options: options)?.upperBound else { return nil }
        return self[lower...]
    }
}

Usage:

let name = sentence.substring(between: "@name:", and: "\n")  // " a name"

let sumary = sentence.substring(after: "@summary:")  // " a paragraph of text\n\n{{something}}\n\na whole bunch of multi-line text"

You can use regular expressions as well:

let name = sentence.substring(between: "@\\w+:", and: "\\n", options: .regularExpression)  // " a name"
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
  • 1
    Wow! This was incredibly useful and instructive, thank you. I'm slowly getting a hang of it but not quite there. The documentation syntax throws me off - when I read a function documentation in swift I struggle to convert that to actual code. Maybe it's a matter of getting used to it and seeing examples like this. – Gerry May 02 '20 at 14:21
  • If you need to find all occurrences https://stackoverflow.com/a/61554215/2303865 – Leo Dabus May 02 '20 at 14:24