【问题标题】:Capture string in regex replacement在正则表达式替换中捕获字符串
【发布时间】:2016-05-24 01:40:31
【问题描述】:

根据我从有关 regex 的 Pharo 文档中收集到的信息,我可以定义一个正则表达式对象,例如:

re := '(foo|re)bar' asRegex

我可以通过这个用字符串替换匹配的正则表达式:

re copy: 'foobar blah rebar' replacingMatchesWith: 'meh'

这将导致:`'meh blah meh'。

到目前为止,一切都很好。但我想替换 'bar' 并保留前缀。因此,我需要一个变量来处理捕获的括号:

re copy: 'foobar blah rebar' replacingMatchesWith: '%1meh'

我想要结果:'foomeh blah remeh'。但是,这只是给了我:'%1meh blah %1meh'。我还尝试使用\1,或\\1,或$1,或{1},结果得到了文字字符串替换,例如'\1meh blah \1meh'

我可以在 GNU Smalltalk 中轻松做到这一点:

'foobar blah rebar' replacingAllRegex: '(foo|re)bar' with: '%1meh'

但我在 Pharo 正则表达式文档中找不到任何地方告诉我如何在 Pharo 中执行此操作。我也为 Pharo 正则表达式做了一堆谷歌搜索,但没有发现任何东西。此功能是 RxMatcher 类还是其他 Pharo 正则表达式类的一部分?

【问题讨论】:

  • pharo 似乎不支持用捕获组替换
  • 那么,您尝试过通常的反向引用样式吗?喜欢\1\\1$1(也许是matchesReplacedWith)?支持捕获组,从 Pharo 中可以做什么匹配很清楚,但没有提示是否支持将反向引用作为替换模式的一部分。
  • @WiktorStribiżew 是的,我也试过\1\\1$1。在每种情况下,替换都是文字字符串。我更新了我的问题,表明这些尝试。我看到就匹配而言,支持捕获组。文档中有用于捕获和枚举捕获的示例。但是,没有关于在替换字符串中反向引用它们。这对我来说似乎是正则表达式查找/替换的基础,所以我很惊讶它不受支持。

标签: regex smalltalk pharo


【解决方案1】:

在对RxMatcher 类进行了一些试验后,我对RxMatcher#copyStream:to:replacingMatchesWith: 选择器进行了以下修改:

copyStream: aStream to: writeStream replacingMatchesWith: aString
    "Copy the contents of <aStream> on the <writeStream>,
     except for the matches. Replace each match with <aString>."

    | searchStart matchStart matchEnd |
    stream := aStream.
    markerPositions := nil.
    [searchStart := aStream position.
    self proceedSearchingStream: aStream] whileTrue: [ | ws rep |
        matchStart := (self subBeginning: 1) first.
        matchEnd := (self subEnd: 1) first.
        aStream position: searchStart.
        searchStart to: matchStart - 1 do:
            [:ignoredPos | writeStream nextPut: aStream next].

        "------- The following lines replaced: writeStream nextPutAll: aString ------"
        "Do the regex replacement including lookback substitutions"
        writeStream nextPutAll: (aString format: self subexpressionStrings).
        "-------"

        aStream position: matchEnd.
        "Be extra careful about successful matches which consume no input.
        After those, make sure to advance or finish if already at end."
        matchEnd = searchStart ifTrue: 
            [aStream atEnd
                ifTrue: [^self "rest after end of whileTrue: block is a no-op if atEnd"]
                ifFalse:    [writeStream nextPut: aStream next]]].
    aStream position: searchStart.
    [aStream atEnd] whileFalse: [writeStream nextPut: aStream next]

然后是“访问”类别:

subexpressionStrings
   "Create an array of lookback strings"
   | ws |
   ws := Array new writeStream.
   2 to: (self subexpressionCount) do: [ :n | | se |
      ws nextPut: ((se := self subexpression: n) ifNil: [ '' ] ifNotNil: [ se ]) ].
   ^ws contents.

通过此修改,我可以使用 Smalltalk String#format: 模式作为参数对替换字符串进行回顾:

re := '((foo|re)ba(r|m))' asRegex
re copy: 'foobar meh rebam' replacingMatchesWith: '{2}bu{3} (was {1})'

结果:

'foobur (was foobar) meh rebum (was rebam)'

【讨论】:

    【解决方案2】:

    您检查过正则表达式帮助吗?没有#replacingAllRegex:,但是匹配器有#subexpression:

    【讨论】:

    • 这真的不是评论吗? ;) 我阅读了我能找到的关于 Pharo 正则表达式的所有在线文档(几乎都是重复的相同实例)。我知道Pharo 中没有#replacingAllRegex:。我引用它作为我在 GNU Smalltalk 中可以做的一个例子。我知道匹配器有#subexpression:,但是没有选择器来执行正则表达式替换,它引用了那些子表达式匹配并且它们存在于其他语言的正则表达式库中(包括 GNU Smalltalk)。如果我记错了,你能给我举个例子吗?
    猜你喜欢
    • 2012-04-11
    • 1970-01-01
    • 2020-08-24
    • 2018-07-13
    • 2017-02-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多