只匹配自己的正则表达式


338

正则表达式(自匹配正则表达式,正则表达式验证正则表达式)面临一些非常酷的挑战

这可能是不可能的,但是是否存在只能匹配自身的正则表达式?

注意,必须包括定界符:

例如/thing/必须匹配/thing/而不是thing。表达式唯一可能的匹配必须是表达式本身。许多语言允许使用字符串代替正则表达式。例如在Go中

package main

import "fmt"
import "regexp"

func main() {

    var foo = regexp.MustCompile("bar")
    fmt.Println(foo.MatchString("foobar"))
}

但是为了挑战,如果您想将引号作为分隔符,则让表达式被定界(开始符号,表达式,结束符号ex:/fancypantpattern/或@[^2048]@)。我认为,鉴于此问题的明显困难,它不会带来太大变化。

为了帮助您:

我为rubular.com(用于ruby regex编辑的网页)放在一起的快速技巧:

var test = document.getElementById("test")
,regex = document.getElementById("regex")
,delimiter="/"
,options = document.getElementById("options")
,delay = function(){test.value = delimiter + regex.value + delimiter + options.value}
,update = function(e){
    // without delay value = not updated value
    window.setTimeout(delay,0);
}
regex.onkeydown = update;
options.onkeydown = update;

即使从技术上讲这是“代码高尔夫”,但如果有人能找到答案/证明这是不可能的话,我将印象深刻。

链接现已修复。对不起

迄今为止的获奖答案:40个字符的jimmy23013


3
显然,任何仅包含文字的正则表达式都可以使用://,/ a /,/ xyz /等。最好要求正则表达式必须包含非文字操作。
— 面包箱2014年

9
文字将不起作用,因为您需要匹配反斜杠,例如/ aaa /将匹配,aaa但/ aaa /不匹配
— Dylan Madisetti 2014年

2
@DylanMadisetti我们必须使用//定界符,还是可以选择其他定界符(PCRE支持几乎任何字符,特别是可以使用匹配的括号/大括号/括号作为定界符)。
— 马丁·恩德

3
我认为这是一个很好的数学/计算问题,证明可能并不容易...许多重要的定理只是作为一个简单的问题而展开的,因此也许在5年后,维基百科将发表文章“ Madisetti问题”;)
— 帕维尔Tokarz

3
对,就是这样。在某些语言中(例如bash中的grep),分隔符本质上是一个空字符串。因此,首先假设正则表达式需要定界符已经是错误的。实际上,由于grep是regexp的最早实现之一,因此regexp的规范定义没有定界符。此假设的最错误体现是PHP,它需要两个定界符:"/和/"
— slebetman 2014年

Answers:


588

PCRE风味,261 289 210 184 127 109 71 53 51 44 40字节

对的,这是可能的!

<^<()(?R){2}>\z|\1\Q^<()(?R){2}>\z|\1\Q>

在这里尝试。(但是/显示为Regex101的分隔符。)

请不要在Regex101页面上进行不必要的编辑(更新)。如果您的编辑实际上并不涉及改进,尝试或测试此正则表达式,则可以对其进行分叉或从其主页创建新的正则表达式。

该版本在Regex101(44字节)上可以更正确地工作:

/^\/()(?R){2}\/\z|\1\Q^\/()(?R){2}\/\z|\1\Q/

在这里尝试。

这比原始版本要简单得多,并且工作起来更像传统的藜。它尝试不使用字符串就定义一个字符串,并在其他地方使用它。因此,可以将其放置在非常接近正则表达式的一端,以减少需要更多字符来定义匹配模式并重复多次的字符数。

说明:

  • \Q^\/()(?R){2}\/\z|\1\Q匹配字符串^\/()(?R){2}\/\z|\1\Q。这使用了一个\Q...\E不需要关闭的怪癖,而未转义的定界符在中起作用\Q。这使得某些以前的版本只能在Regex101上运行,而不能在本地运行。但是幸运的是最新版本有效,并且我使用此版本获得了更多字节。
  • \1在\Q与捕获的组1匹配之前。由于此选项中不存在组1,因此只能在递归调用中进行匹配。在递归调用中,它匹配空字符串。
  • (?R){2}递归调用整个正则表达式两次,^\/()(?R){2}\/\z|\1\Q每次都匹配。
  • () 除了将空字符串捕获到组1中之外,不会执行任何其他操作,这将启用递归调用中的另一个选项。
  • ^\/()(?R){2}\/\z(?R){2}从头到尾与添加的定界符匹配。在\/之前的递归调用还确保这个选项本身并不在递归调用匹配,因为它不会在该字符串的开头。

51个字节,已关闭\Q...\E:

/\QE\1|^\/(\\)Q(?R){2}z\/\E\1|^\/(\\)Q(?R){2}z\/\z/

在这里尝试。

原始版本,188字节

感谢MartinBüttner打高尔夫球约100个字节!

/^(?=.{173}\Q\2\)){2}.{11}$\E\/\z)((?=(.2.|))\2\/\2\^\2\(\2\?=\2\.\2\{173}\2\\Q\2\\2\2\\\2\)\2\)\2\{2}\2\.\2\{11}\2\$\2\\E\2\\\2\/\2\\z\2\)\2\(\2\(\2\?=\2\(\2\.2\2\.\2\|\2\)\2\)){2}.{11}$/

在这里尝试。

或210个字节,不包含\Q...\E:

/^(?=.{194}\\2\\.\)\{2}\.\{12}\$\/D$)((?=(.2.|))\2\/\2\^\2\(\2\?=\2\.\2\{194}\2\\\2\\2\2\\\2\\\2\.\2\\\2\)\2\\\2\{2}\2\\\2\.\2\\\2\{12}\2\\\2\$\2\\\2\/D\2\$\2\)\2\(\2\(\2\?=\2\(\2\.2\2\.\2\|\2\)\2\)){2}.{12}$/D

在这里尝试。

扩展版本:

/^(?=.{173}\Q\2\)){2}.{11}$\E\/\z)        # Match things near the end.
((?=(.2.|))                               # Capture an empty string or \2\ into group 2.
   \2\/\2\^\2\(\2\?=\2\.\2\{173}\2\\Q\2\\2\2\\\2\)\2\)\2\{2}\2\.
   \2\{11}\2\$\2\\E\2\\\2\/\2\\z\2\)      # 1st line escaped.
   \2\(\2\(\2\?=\2\(\2\.2\2\.\2\|\2\)\2\) # 2nd line escaped.
){2}
.{11}$/x

像扩展(?=并\1取得了所谓的“正规”的表述不再普通,这也使得基内斯可能。反向引用不是常规的,但提前。

说明:

  • 我用来\2\代替\转义特殊字符。如果\2匹配空字符串,\2\x(其中x是一个特殊字符)匹配其x自身。如果\2匹配\2\,则\2\x匹配转义的那一个。\2组1的两次匹配中的正则表达式可以不同。第一次\2应匹配空字符串,第二次应匹配\2\。
  • \Q\2\)){2}.{11}$\E\/\z(第1行)从末尾匹配15个字符。和.{11}$(第7行)从末尾(或尾随换行符之前)匹配11个字符。因此,第二个模式之前的模式必须与第一个模式中的前4个或3个字符\2\.\2\|\2\)\2\)匹配,因此必须匹配...\2\)或...\2\。不能有结尾的换行符,因为最后一个字符应该是)。并且匹配的文字)在最右边的文字之前不包含其他文字,因此所有其他字符都必须在中\2。\2定义为(.2.|),因此只能为\2\。
  • 第一行使整个表达式精确匹配188个字符,因为所有内容的长度都是固定的。组1的两次匹配45 * 2个字符加29次\2。第1组之后的内容匹配11个字符。因此,两次的总长度\2必须恰好是3个字符。\2第二次知道是3个字符长,第一次必须为空。
  • 除了前瞻和以外的所有内容都是第\21组中的文字。\2已知两次,并且从第一行知道了最后几个字符,此正则表达式恰好匹配一个字符串。
  • 马丁·布特纳(MartinBüttner)提出了使用先行捕捉第二组并将其与奎纳部分重叠的想法。这删除了第1组两次之间未以常规方式逃脱的字符,并帮助避免了与我的原始版本中的字符匹配的模式,并大大简化了正则表达式。

不带递归或反向引用的正则表达式,85字节

有人可能会争辩说,具有递归或反向引用的表达式不是真正的“常规”表达式。但是仅具有超前性的表达式仍只能匹配正则语言,尽管如果用传统正则表达式表达的话可能更长。

/(?=.*(\QE\\){2}z\/\z)^\/\(\?\=\.\*\(\\Q.{76}\E\\){2}z\/\z)^\/\(\?\=\.\*\(\\Q.{76}\z/

在这里尝试。

610个字节\Q...\E(不打高尔夫球):

/^(?=.{610}$)(?=.{71}(\(\.\{8\}\)\?\\.[^(]*){57}\)\{2\}\.\{12\}\$\/D$)((.{8})?\/(.{8})?\^(.{8})?\((.{8})?\?=(.{8})?\.(.{8})?\{610(.{8})?\}(.{8})?\$(.{8})?\)(.{8})?\((.{8})?\?=(.{8})?\.(.{8})?\{71(.{8})?\}(.{8})?\((.{8})?\\(.{8})?\((.{8})?\\(.{8})?\.(.{8})?\\(.{8})?\{8(.{8})?\\(.{8})?\}(.{8})?\\(.{8})?\)(.{8})?\\(.{8})?\?(.{8})?\\(.{8})?\\(.{8})?\.(.{8})?\[(.{8})?\^(.{8})?\((.{8})?\](.{8})?\*(.{8})?\)(.{8})?\{57(.{8})?\}(.{8})?\\(.{8})?\)(.{8})?\\(.{8})?\{2(.{8})?\\(.{8})?\}(.{8})?\\(.{8})?\.(.{8})?\\(.{8})?\{12(.{8})?\\(.{8})?\}(.{8})?\\(.{8})?\$(.{8})?\\(.{8})?\/D(.{8})?\$(.{8})?\)(.{8})?\(){2}.{12}$/D

在这里尝试。

这个想法是相似的。

/^(?=.{610}$)(?=.{71}(\(\.\{8\}\)\?\\.[^(]*){57}\)\{2\}\.\{12\}\$\/D$)
((.{8})?\/(.{8})?\^(.{8})?\((.{8})?\?=(.{8})?\.(.{8})?\{610(.{8})?\}(.{8})?\$(.{8})?\)
(.{8})?\((.{8})?\?=(.{8})?\.(.{8})?\{71(.{8})?\}
  (.{8})?\((.{8})?\\(.{8})?\((.{8})?\\(.{8})?\.(.{8})?\\(.{8})?\{8(.{8})?\\(.{8})?\}
    (.{8})?\\(.{8})?\)(.{8})?\\(.{8})?\?(.{8})?\\(.{8})?\\
    (.{8})?\.(.{8})?\[(.{8})?\^(.{8})?\((.{8})?\](.{8})?\*(.{8})?\)(.{8})?\{57(.{8})?\}
  (.{8})?\\(.{8})?\)(.{8})?\\(.{8})?\{2(.{8})?\\(.{8})?\}
  (.{8})?\\(.{8})?\.(.{8})?\\(.{8})?\{12(.{8})?\\(.{8})?\}
  (.{8})?\\(.{8})?\$(.{8})?\\(.{8})?\/D(.{8})?\$(.{8})?\)(.{8})?\(){2}.{12}$/D

基本正则表达式

如果不允许前瞻,那么我现在能做的就是:

/\\(\\\(\\\\){2}/

哪个匹配

\\(\\\(\\

如果{m,n}不允许使用量词,那是不可能的,因为没有什么可以匹配一个字符串,也不能匹配比其更长的字符串。当然,人们仍然可以发明\q只匹配的东西/\q/,并且仍然用该正则表达式来表达。但是显然,主要的实现都不能支持这种情况。


5
令人印象深刻。我花了一段时间尝试使它与其他匹配,但没有成功。
— primo 2014年

76
人怎么会(地狱)产生这样的东西?
— xem 2014年

61
这应该是该网站上投票率最高的答案。
— Cruncher 2014年

44
这是我见过的最荒谬,不可思议的事情。
— Alex A.

22
有人发布了此信息,因此我一天获得49票赞成票……
— jimmy23013 '16
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.