【问题标题】:Swift NSRegularExpression is too greedySwift NSRegularExpression 太贪心了
【发布时间】:2021-05-28 22:11:18
【问题描述】:

我有一个正则表达式,应该允许我在 Markdown 文档中注释代码片段。基本上它在/*HLS*//*HLE*/ cmets 之间寻找内容,并将其包装在span 中。它甚至允许一个小的解释,这将成为跨度的标题。

import Foundation

let content = """
extension ViewController: UITableViewDataSource {
  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return /*HLS Explanation here!*/viewModel.books.value.count/*HLE*/
  }

  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let book = /*HLS*/viewModel.books.value[indexPath.row]/*HLE*/
    let cell = tableView.dequeueReusableCell(withIdentifier: "BookCell") as! BookCell
    cell.configure(with: book)
    return cell
  }
}
"""

let regex = try NSRegularExpression(pattern: #"(?s)\/\*HLS\W?(.*?)\*\/(.*?)\/\*HLE\*\/"#)
let range = NSRange(content.startIndex..<content.endIndex, in: content)

let newContent = regex.stringByReplacingMatches(in: content, options: [], range: range, withTemplate: #"<span class="highlight" title="$1">$2</span>"#)
print(newContent)

结果:

extension ViewController: UITableViewDataSource {
  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return <span class="highlight" title="Explanation here!">viewModel.books.value.count</span>
  }

  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let book = <span class="highlight" title="">viewModel.books.value[indexPath.row]</span>
    let cell = tableView.dequeueReusableCell(withIdentifier: "BookCell") as! BookCell
    cell.configure(with: book)
    return cell
  }
}

这正是它应该如何工作的????

但是,当我从第一条评论中删除 Explanation here! 时,正则表达式太贪婪了。

import Foundation

let content = """
extension ViewController: UITableViewDataSource {
  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return /*HLS*/viewModel.books.value.count/*HLE*/
  }

  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let book = /*HLS*/viewModel.books.value[indexPath.row]/*HLE*/
    let cell = tableView.dequeueReusableCell(withIdentifier: "BookCell") as! BookCell
    cell.configure(with: book)
    return cell
  }
}
"""

let regex = try NSRegularExpression(pattern: #"(?s)\/\*HLS\W?(.*?)\*\/(.*?)\/\*HLE\*\/"#)
let range = NSRange(content.startIndex..<content.endIndex, in: content)

let newContent = regex.stringByReplacingMatches(in: content, options: [], range: range, withTemplate: #"<span class="highlight" title="$1">$2</span>"#)
print(newContent)

结果:

extension ViewController: UITableViewDataSource {
  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return <span class="highlight" title="/viewModel.books.value.count/*HLE">
  }

  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let book = /*HLS*/viewModel.books.value[indexPath.row]</span>
    let cell = tableView.dequeueReusableCell(withIdentifier: "BookCell") as! BookCell
    cell.configure(with: book)
    return cell
  }
}

如您所见,viewModel.books.value.count/*HLE 成为标题,然后是 second /*HLE*/ 之前的所有内容。正则表达式应该匹配标题捕获组,直到它遇到的第一个 */,但它不是 - 它一直到第二个。这是为什么?正则表达式应该匹配(.*?)直到它遇到\*\/,对吧?

当我删除 (?s) 标志时,一切都会再次按预期工作,但我希望能够在 /*HLS*//*HLE*/ 之间换行。

【问题讨论】:

    标签: swift nsregularexpression


    【解决方案1】:

    问题在于\W? "non-word" pattern 部分:它可以选择匹配除字母、数字、下划线和某些字符(如变音符号或连接符标点符号和零宽度连接符)以外的任何字符。

    有几种解决方案,但您可能只想匹配任何非单词字符,但紧跟在HLS 之后的*/ 子字符串。因此,您可以立即使用此修复方法:

    (?s)/\*HLS(?:(?!\*/)\W)?(.*?)\*/(.*?)/\*HLE\*/
    

    请参阅regex demo(?:(?!\*/)\W)? 可选(末尾为?)非捕获组((?:...))匹配一个或零个不是* 的非单词字符,紧跟/

    注意您不需要转义正斜杠,它们不是任何特殊的正则表达式元字符,并且您不需要在 Swift 代码中转义它们,因为正则表达式仅使用字符串文字定义,而不是使用通常需要/.../ 表示法的正则表达式文字(其中/正则表达式分隔符)。

    如果您想让模式更安全(排除“损坏的”HLS/HLE 上的匹配项),您可以使用类似的解决方案

    (?s)/\*HLS(?:(?!\*/)\W)?((?:(?!/\*HLS).)*?)\*/(.*?)/\*HLE\*/
    

    参见this regex demo,我在其中将/*HLS 添加到字符串文字中。 (?:(?!/\*HLS).)*? 部分匹配任何不启动 /*HLS 字符序列的字符,零个或多个但尽可能少地出现。

    请注意,如果您在字符串文字中有匹配项,则整个正则表达式将无法正常工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-09-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多