正则表达式游戏 - 用可变数量的字符替换除特定单词之外的每个单词

Gaw*_*wil 7 regex

嘿那里,你是正则表达的恋人!

这些时候,我在Regex中,并且有一个纯粹的理论问题.简单来说,我会把它作为一个游戏来呈现.

游戏:
假设你有一个由空格分隔的单词列表.
我称之为单词是因为它们是由正则表达式定义的:( [a-zA-Z_0-9]+这里没有空字)
列表示例:
Horse Banana Joker RoXx0r A_Long_Word Joker 1337

我要你做的是将除了Joker之外的每个单词替换为$等于字符数的$匹配的单词.
通过我们之前的列表,我们将获得:
$$$$$ $$$$$$ Joker $$$$$$ $$$$$$$$$$$ Joker $$$$

用更少的单词: 我想要一个正则表达式匹配每个不属于单词"Joker"的字符(在字符串中,我的意思是,不是那个组成单词Joker)

虽然这并不容易,这不是不可能的(我有自己的正则表达式).这就是为什么我会制定一些规则.

规则 :

  • 它必须只有1个正则表达式
  • 我不接受任何仅适用于特定语言的正则表达式
  • 我仍然会接受最常见的功能,如Conditional,Lookarounds等......即使某些语言无法读取它们
  • 不允许递归(但如果你有一个递归的递归,发布它,只是为了正则表达式的美丽^^)
  • 必须针对性能优化正则表达式
  • 如果你的正则表达式匹配(得到它?;))这些规则但不满足我,我会随意添加一些规则

添加规则:

  • 没有



为了帮助你,这里有正则表达式必须工作的一些字符串:
Horse Banana Joker RoXx0r A_Long_Word Joker 1337 Joke Poker Joker Jokers
更换
$$$$$ $$$$$$ Joker $$$$$$ $$$$$$$$$$$ Joker $$$$ $$$$ $$$$$ Joker $$$$$$

Joker Joker Joker
后必须返回:更换后必须返回:
Joker Joker Joker

再次,解决问题不是这里的目标,我希望看到不同的解决方案,更重要的是我希望看到最好的解决方案!


解决方案:

一个非常优雅的一个由卡西米尔等伊波利特:
(?:\G(?!^)|(?<!\S)(?!Joker(?:\s|$)))\S(更换:$)
查看信息
.然而\ G变走的乐趣出了问题,并在每一种语言是不行的,所以除非是有可能创建我无法接受自定义分隔符是相当于一个\ g

几乎公认的答案也卡西米尔等伊波利特:
((?:\s+|\bJoker\b)*)\S((?:\s+Joker)*\s*$)?(更换:$1$$2)
查看信息
时,有字符串中唯一的小丑的话也不行

类似的解决方案通过ClasG:
(\bJoker[^\w]+)\w|\w([^\w]+Joker\b)|\w(更换:$1$$2)
查看信息
当有字符串中唯一的小丑的话不起作用

另一条由ClasG:
[^Joker\s]|(?<!\b)J|J(?!oker\b)|(?<!\bJ)o|o(?!ker\b)|(?<!\bJo)k|k(?!er\b)|(?<!\bJok)e|e(?!r\b)|(?<!\bJoke)r|r(?!\b)(更换:$)
查看信息
不是很有效,虽然,但它是看待事物的另一种方式;)

我想出了一个类似的正则表达式看完后评论下面的Rahul :(
(?(?<=\b|\bJ|\bJo|\bJok|\bJoke|\bJoker)(?!(?:Joke|oke|ke|e|)r\b)\w|\w)替换$)
Regex101
这也是低效的,但使用相同的外观列表的东西:)

这是我的第一个解决方案:
我使用的技巧可能被视为作弊,但我没有,因为它不会改变你用来替换字符的功能.在将字符替换为字符串之前,您只需在字符串末尾添加"$".
因此,而不是像这样:
string = replace(string, regex, '$1$2')
我们将有:
string = replace(string+'$', regex, '$1$2')

因此,这里的正则表达式:
(\bJoker\b)|.$|\w(?=.*(\$))(更换:$1$2)
正则表达式101
这应该与除了那些不支持向前看符号所有语言(他们是相当罕见的),


请在发布新的正则表达式,如果你找到的,我想看到更多的方法来做到这一点!:)

Cas*_*yte 4

适用于 PCRE/Perl/Ruby/Java/.net

寻找:

(?:\G(?!^)|(?<!\S)(?!Joker(?!\S)))\S
Run Code Online (Sandbox Code Playgroud)

代替:

$
Run Code Online (Sandbox Code Playgroud)

演示

图案细节:

(?:
    \G (?!^) # contigous to a previous match (but not at the start of the string)
  |        # OR
    (?<!\S)  # not preceded by a non white-space
    (?!Joker(?!\S)) # not followed by the forbidden word
)
\S   # a non-whitespace character
Run Code Online (Sandbox Code Playgroud)

如果您的单词仅由单词字符组成,您可以简化使用单词和非单词边界的模式:(?:\G\B|\b(?!Joker\b))\w


其他方式(PCRE/Perl):没有该\G功能并带有回溯控制动词(*SKIP)(需要更少的步骤):

\s*(?:Joker(?:\s+|$))*(*SKIP)\K.
Run Code Online (Sandbox Code Playgroud)

(*SKIP)仅当字符串以禁止词或空格结尾时,清晰才有用。您也可以将其替换为(*COMMIT).

演示

或者:

\bJoker\b(*SKIP)(*F)|\S
Run Code Online (Sandbox Code Playgroud)

并使用 pypi python 正则表达式模块(该模块有一个用于开头的单词边界和一个用于单词结尾的单词边界):

\mJoker\M(*SKIP)(*F)|\S
Run Code Online (Sandbox Code Playgroud)

一个与 Javascript 一起使用的(如果有东西需要替换的话)

寻找:

((?:\s+|\bJoker\b)*)\S((?:\s+Joker)*\s*$)?
Run Code Online (Sandbox Code Playgroud)

替换:(对组 1 的反向引用,转义 $,对组 2 的反向引用)

$1$$$2 
Run Code Online (Sandbox Code Playgroud)

演示


另一种 Javascript 版本使用 y 标志(强制匹配连续),但不幸的是,Internet Explorer、Safari 和移动浏览器(除 Firefox mobile 外)不支持此版本:

var strs = ['Horse Banana Joker RoXx0r A_Long_Word Joker 1337 Joke Poker Joker', 'Joker Joker Joker'];

strs.forEach(function (s) {
    console.log(s.replace(/(?=((?:\s+|\bJoker\b)*))\1./gy, '$1$$'));
});
Run Code Online (Sandbox Code Playgroud)

模拟(?=(...))\1一个原子组(禁止回溯)。