【问题标题】:How to replace pattern of repeating characters/words only at the beginning of the string?如何仅在字符串的开头替换重复字符/单词的模式?
【发布时间】:2015-10-08 18:28:27
【问题描述】:

请注意,这个问题是在 Julia 的上下文中,因此(据我所知)PCRE。

假设你有一个这样的字符串:

"sssppaaasspaapppssss"

并且您想单独匹配字符串末尾的重复字符(在我们的字符串的情况下,四个“s”字符 - 也就是说, matchall 给出 ["s","s" ,"s","s"],而不是 ["ssss"])。这很简单:

r"(.)(?=\1*$)"

它实际上是微不足道的(并且易于使用 - replace(r"(.)(?=\1*$)","hell","k") 将提供 "hekk"replace(r"(.)(?=\1*$)","hello","k") 将提供 "hellk")。并且可以通过将点换成更复杂的东西来推广重复模式:

r"(\S+)(?=( \1)*$)"

例如,它将独立匹配"abc abc defg abc h abc abc abc" 中“abc”的最后三个实例。

这就引出了一个问题……你将如何匹配字符串开始处的重复字符或模式?具体来说,以上面使用的方式使用正则表达式。

显而易见的方法是将上述正则表达式的方向反转为 r"(?<=^\1*)(.)" - 但 PCRE/Julia 不允许后向具有可变长度(除非它是固定变量,如 (?<=ab|cde)),因此引发错误。下一个想法是使用“\K”作为r"^\1*\K(.)" 的内容,但这只能匹配第一个字符(可能是因为它在匹配后“前进”,不再匹配插入符号)。

为了清楚起见:我正在寻找一个正则表达式,例如,导致

replace("abc abc defg abc h abc abc abc",<regex here>,"hello")

生产

"hello hello defg abc h abc abc abc"

如您所见,它从一开始就将每个“abc”替换为“hello”,但直到第一个不匹配为止。我在上面提供的相反的一个在字符串的另一端执行此操作:

replace("abc abc defg abc h abc abc abc",r"(\S+)(?=( \1)*$)","hello")

生产

"abc abc defg abc h hello hello hello"

【问题讨论】:

  • 使用这个^(\S+)(?:\s+\1)*,然后对空格字符进行分割,
  • @AvinashRaj - 如果您通读完整的问题,您会发现我想知道它是否可以在没有多个步骤的情况下完成 - 也就是说,只需使用正则表达式。我可以使用字符串结尾等效项,r"(.)(?=\1*$)"(或更一般的r"(\S+)(?=( \1)*$)")来做到这一点。
  • 反转字符串。 ;-)
  • @KingMob - 如果您只想匹配一个方向,您可以这样做。但这不是正则表达式解决方案,如果您想在两个方向上匹配(例如,捕获字符串"shorts shoes shop shovel shortstuff shoplifter shopshop" 的每个部分中的第一个sho,而不是第二个sho shopshop,你需要从两个方向看 - 从某种意义上说,你基本上是在弄清楚哪个部分不是分隔符)
  • 好吧,您可以用"hello$2" 替换(\S+)((?= \1)|.*)(例如JavaScript),但不幸的是,您需要能够引用AFAIK 在Julia 中无法做到的替换字符串中的子匹配项:-(

标签: regex julia pcre regex-lookarounds lookbehind


【解决方案1】:

您可以使用\G 锚点来匹配上一个匹配项之后或字符串开头的位置。通过这种方式,您可以确保从字符串开头到最后一次出现的结果的连续性:

\G(\S+)( (?=\1 ))?

demo

或者能够匹配到字符串的结尾:

\G(\S+)( (?=\1(?: |\z)))?

【讨论】:

  • +1 - 这当然是个好主意,一旦 Julia 添加了 PCRE2 替换功能,我认为它可以让我做我想做的事......不幸的是,它不会同时给我想要的功能,因为它也占据了空间。
  • @GlenO:我不认为在替换字符串中使用对捕获组的引用是 PCRE2 特有的。我没有找到使用 julia 的方法(我不知道这种语言),也没有找到文档中包含捕获的任何示例,因此如果不可能,则它更多是实现的特殊性。不管怎样,一个简单的解决方法是使用" hello" 作为替换字符串并用lstrip() 修剪字符串(或删除第一个字符的另一种方法)。如果这个限制真的存在,我认为没有其他办法了。
  • @GlenO:我在某处读到replace 可以使用函数作为替换,但我不知道默认参数是整个匹配还是匹配对象(具有可用的捕获组)以及是否允许用户定义的函数。
  • 不幸的是,这是整个匹配,而不是匹配对象。而且我知道有一些解决方法。正如我所说,我真正希望的是一种方法来实现可以在字符串末尾完成的相同操作 - 很容易匹配字符串末尾的重复模式......但在开始时,我见过的最好的是你的把戏,如果模式没有很好的分隔符,它就不起作用。如果没有人提供能够在接下来的几天内完美完成的解决方案,我将在此时接受您的最佳答案。
  • 是的,这假设存在已知的分隔符(已暗示),因此不适用于问题中的第一个字符串。它也不适用于字符串“abc abc”,但如果空格不是重复的一部分,那么它不应该。 \1( |$) 可能会解决这个问题。我认为此时我们需要一个更明确的问题。
【解决方案2】:

对于 PCRE 风格的引擎,遗憾的是没有办法做到这一点
可变长度后视。

纯粹的解决方案是不可能的。
没有 \G 锚诡计可以做到这一点。

这就是 \G 锚不起作用的原因。

有了锚,你唯一的保证就是最后一场比赛
导致前向重叠被检查为相等的匹配
到当前比赛。

因此,您只能从头开始全局匹配最多 N-1 个重复项。

这是一个证明:

正则表达式:

 # (?:\G([a-c]+)(?=\1))

 (?:
      \G 
      ( [a-c]+ )                    # (1)
      (?=
           \1 
      )
 )

输入:

abcabcabcbca

输出:

 **  Grp 0 -  ( pos 0 , len 3 ) 
abc  
 **  Grp 1 -  ( pos 0 , len 3 ) 
abc  
------------
 **  Grp 0 -  ( pos 3 , len 3 ) 
abc  
 **  Grp 1 -  ( pos 3 , len 3 ) 
abc  

结论:

即使您知道N 个在前一个前瞻中存在,
Nth 没有当前前瞻的条件就无法匹配。

对不起,祝你好运!
如果您找到纯正则表达式解决方案,请告诉我。

【讨论】:

  • 我认为您应该澄清一下,在没有明确分隔符的情况下,无法进行此类替换。虽然评论中提到了这一点,但问题并不清楚。
  • 顺便说一句,这只能通过条件替换来实现,这在 Boost, AFAIK 中可用。这是实现中很少见的功能。
  • @nhahtdh - 就明确的分隔符而言,这就是这里的谬误。如果前瞻是可选的,那么它将匹配单个实例。我提供了一个最低限度的证明,证明没有任何方法是有效的。不这样想是概念上的错误。人们不接受事实,试图从石头中取血,这让我有点恼火。
  • 对于带分隔符的情况,我猜是这样的? regex101.com/r/bV0uA5/1^(\S+)( (?=\1(?: |$)))|(?!^)\G(\S+)( (?=\3(?: |$)))? 和替换hello$2$4
  • OP 确实在寻找通用解决方案。我只挑剔一个事实,虽然一般的解决方案是不可能的,但如果有明确的分隔符,那么它是可能的。
猜你喜欢
  • 2021-07-09
  • 1970-01-01
  • 1970-01-01
  • 2023-01-07
  • 1970-01-01
  • 2011-02-07
  • 1970-01-01
  • 1970-01-01
  • 2017-12-20
相关资源
最近更新 更多