Swift 5.1 - 是否有一种干净的方法来处理子字符串/模式匹配的位置

Swift 5.1 - is there a clean way to deal with locations of substrings/ pattern matches

我对 Swift 非常非常陌生,并且承认它的某些结构很费力。我必须使用一个文本文件并进行许多操作——这里有一个例子来说明这一点:

假设我有这样的文本(多行)

   Mary had a little lamb

   @name: a name

   @summary: a paragraph of text 

   {{something}}

   a whole bunch of multi-line text

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

我希望能够做一些简单的事情,比如找到@name 的位置,然后拆分它以获得名称等等。我在 javascript 中完成了此操作,使用 substr 和正则表达式匹配非常简单。

在 swift 中,这应该是 swift 并且很简单,但我发现这非常令人困惑。

有人可以帮忙做一下吗

  1. 查找子字符串的开始位置

  2. 提取子字符串末尾到文本末尾之间的所有文本

抱歉,如果这是微不足道的 - 但 Apple 文档感觉非常复杂,而且很多示例都是多年以前的。我似乎也找不到正则表达式的简单应用。

您可以使用 range()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"

您可以使用 string range(of: String) 方法来查找字符串的范围,获取其 upperBound 并从字符串的该位置搜索行尾:


游乐场测试:

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

如果您需要从那里获取字符串到字符串末尾,您可以使用 PartialRangeFrom:

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

如果你发现自己经常使用它,你可以扩展 StringProtocol 并创建你自己的方法:

 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...]
    }
}

用法:

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"

您也可以使用正则表达式:

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