Regex ที่ตรงกับตัวเองเท่านั้น


338

มีความท้าทายที่น่าสนใจเกี่ยวกับ regex ( การจับคู่ด้วยตนเอง , regex validating regex )

สิ่งนี้อาจเป็นไปไม่ได้ แต่มี regex ที่จะจับคู่ตัวเองเท่านั้นหรือไม่

หมายเหตุจะต้องมีตัวคั่น:

เช่น/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;

แม้ว่านี่จะเป็น 'code golf' ในทางเทคนิคฉันจะต้องประทับใจมากถ้าใครสามารถหาคำตอบ / พิสูจน์ได้ว่ามันเป็นไปไม่ได้

ลิงก์ได้รับการแก้ไขแล้ว ขออภัยที่ทุกคน

คำตอบที่ชนะ: jimmy23013 with 40 ตัวอักษร


3
เห็นได้ชัดว่านิพจน์ทั่วไปใด ๆ ที่รวมตัวอักษรเท่านั้นจะใช้งานได้: //, / a /, / xyz / ฯลฯ อาจเป็นเรื่องดีที่จะกำหนดให้ regex ต้องรวมการดำเนินการที่ไม่ใช่ตัวอักษรด้วย
breadbox

9
ตัวอักษรจะไม่ทำงานเพราะคุณจำเป็นต้องจับคู่แบ็กสแลชเช่น / aaa / จะจับคู่aaaแต่ไม่ใช่ / aaa /
Dylan Madisetti

2
@DylanMadisetti เราต้องใช้//ตัวคั่นหรือเราสามารถเลือกตัวคั่นอื่น ๆ (PCRE รองรับตัวละครมาก ๆ และโดยเฉพาะอย่างยิ่งคุณสามารถใช้วงเล็บ / วงเล็บ / วงเล็บเป็นตัวคั่น)
Martin Ender

3
ฉันคิดว่านี่เป็นปัญหาทางคณิตศาสตร์ / การคำนวณที่ค่อนข้างดีและการพิสูจน์อาจไม่ใช่เรื่องง่าย ... ทฤษฎีบทสำคัญหลายข้อเริ่มต้นเป็นเพียงคำถามง่าย ๆ ที่จะเล่นกับดังนั้นบางทีใน 5 ปีจะมีบทความวิกิพีเดีย "ปัญหา Madisetti";)
Paweł Tokarz

3
ใช่แล้ว ในบางภาษา (คิดว่า grep เป็น bash) ตัวคั่นจะเป็นสตริงว่าง ดังนั้นสมมติว่า regexp ต้องการตัวคั่นนั้นผิดในตอนแรก อันที่จริงเนื่องจาก grep เป็นหนึ่งในการดำเนินการที่เร็วที่สุดของ regexp นิยามที่เป็นที่ยอมรับของ regexp ไม่มีตัวคั่น อาการที่ผิดพลาดที่สุดของสมมติฐานนี้คือ PHP ซึ่งต้องการตัวคั่นสองตัว: "/และ/"
slebetman

คำตอบ:


589

รสชาติ PCRE, 261 289 210 184 127 109 71 53 51 44 40 ไบต์

ใช่มันเป็นไปได้!

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

ลองที่นี่ (แต่/แสดงให้เห็นว่าเป็นตัวคั่นบน Regex101)

โปรดอย่าทำการแก้ไข (อัปเดต) ที่ไม่จำเป็นในหน้า Regex101 หากการแก้ไขของคุณไม่จริงเกี่ยวข้องกับการปรับปรุงหรือพยายามทดสอบ regex นี้คุณสามารถแยกหรือสร้างใหม่จากหน้าแรกของพวกเขา

รุ่นนี้ทำงานอย่างถูกต้องมากขึ้นใน Regex101 (44 ไบต์):

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

ลองที่นี่

นี่ง่ายกว่ารุ่นดั้งเดิมมากและทำงานได้เหมือนควินดั้งเดิม มันพยายามกำหนดสตริงโดยไม่ใช้มันและใช้มันในที่อื่น ดังนั้นจึงสามารถวางไว้ใกล้กับปลายด้านหนึ่งของ regex เพื่อลดจำนวนอักขระที่ต้องใช้อักขระเพิ่มเพื่อกำหนดรูปแบบการจับคู่และทำซ้ำอีกครั้ง

คำอธิบาย:

  • \Q^\/()(?R){2}\/\z|\1\Q^\/()(?R){2}\/\z|\1\Qตรงกับสตริง นี้ใช้มุมแหลมที่\Q...\Eไม่ต้องมีการปิดและตัวคั่นใช้ Escape \Qทำงานใน สิ่งนี้ทำให้บางเวอร์ชันก่อนหน้านี้ใช้งานได้กับ Regex101 เท่านั้นและไม่ใช่ในเครื่อง แต่โชคดีที่เวอร์ชันล่าสุดใช้งานได้และฉันก็เพิ่มจำนวนไบต์อีกด้วย
  • \1ก่อนการ\Qจับคู่กลุ่มที่ถูกจับภาพ 1 เนื่องจากกลุ่มที่ 1 ไม่มีอยู่ในตัวเลือกนี้จึงสามารถจับคู่ในการโทรซ้ำ ในการโทรซ้ำจะจับคู่สตริงว่าง
  • (?R){2}เรียก regex ทั้งหมดซ้ำสองครั้งซึ่งตรงกับ^\/()(?R){2}\/\z|\1\Qแต่ละครั้ง
  • () ไม่ทำอะไรนอกจากจับสตริงว่างลงในกลุ่ม 1 ซึ่งเปิดใช้งานตัวเลือกอื่นในการโทรซ้ำ
  • ^\/()(?R){2}\/\zจับคู่(?R){2}กับตัวคั่นเพิ่มตั้งแต่ต้นจนจบ \/ก่อนที่จะโทรเรียกซ้ำยังทำให้แน่ใจว่าตัวเลือกนี้ตัวเองไม่ตรงกับในการเรียก recursive เพราะมันจะไม่เป็นที่จุดเริ่มต้นของสตริง

51 ไบต์เมื่อปิด\Q...\E:

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

ลองที่นี่

รุ่นดั้งเดิม 188 ไบต์

ขอบคุณ Martin Bü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ทำให้นิพจน์ "ปกติ" ที่เรียกว่าไม่เป็นแบบปกติอีกต่อไปซึ่งทำให้เป็นไปได้ด้วย การอ้างอิงกลับไม่ปกติ แต่ lookahead คือ

คำอธิบาย:

  • ฉันใช้\2\แทน\ตัวละครพิเศษเพื่อหลบหนี หาก\2ตรงกับสตริงว่าง\2\x(ซึ่งxเป็นอักขระพิเศษ) ตรงกับxตัวเอง หาก\2การแข่งขัน\2\, \2\xตรงกับหนึ่งหนี \2ในการจับคู่สองกลุ่ม 1 อาจแตกต่างกันใน regex ในครั้งแรกที่ควรจะตรงกับสตริงที่ว่างเปล่าและครั้งที่สอง\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 ตัวอักษรจะต้องว่างเปล่าเป็นครั้งแรก
  • ทุกอย่างยกเว้น lookahead และ\2เป็นตัวอักษรในกลุ่ม 1 ด้วยสองครั้งที่\2รู้จักและตัวละครสองสามตัวสุดท้ายที่รู้จักจากบรรทัดแรก regex นี้ตรงกับหนึ่งสตริง
  • Martin Büttnerเกิดขึ้นกับแนวคิดของการใช้ lookahead เพื่อจับภาพกลุ่ม 2 และทำให้มันทับซ้อนกับส่วน quine นี่เป็นการลบตัวละครที่ไม่หนีไปตามปกติระหว่างสองครั้งของกลุ่มที่ 1 และช่วยหลีกเลี่ยงรูปแบบที่จะจับคู่พวกเขาในเวอร์ชั่นดั้งเดิมของฉันและทำให้ regex ง่ายขึ้นมาก

Regex ที่ไม่มีการเรียกซ้ำหรือการอ้างอิงย้อนกลับ 85 ไบต์

บางคนอาจโต้แย้งว่านิพจน์ที่มีการเรียกซ้ำหรือการอ้างอิงซ้ำไม่ใช่นิพจน์ "ปกติ" ที่แท้จริง แต่นิพจน์ที่มี lookahead เท่านั้นยังคงสามารถจับคู่ภาษาปกติได้แม้ว่าพวกเขาอาจจะนานกว่านั้นหากแสดงออกด้วยการแสดงออกปกติแบบดั้งเดิม

/(?=.*(\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

การแสดงออกปกติขั้นพื้นฐาน

ถ้าไม่อนุญาต lookahead สิ่งที่ฉันทำได้ตอนนี้คือ:

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

ซึ่งตรงกับ

\\(\\\(\\

หาก{m,n}ไม่ได้รับอนุญาตให้ใช้ปริมาณมันเป็นไปไม่ได้เพราะไม่มีสิ่งใดที่สามารถจับคู่หนึ่งสตริงเท่านั้นสามารถจับคู่สตริงได้นานกว่าตัวมันเอง แน่นอนว่าเรายังคงสามารถประดิษฐ์สิ่งที่เหมือนกัน\qซึ่งตรงกับเท่านั้น/\q/และยังคงพูดถึงนิพจน์ทั่วไป แต่ดูเหมือนไม่มีอะไรเช่นนี้ได้รับการสนับสนุนโดยการใช้งานที่สำคัญ


5
ประทับใจ ฉันใช้เวลาสักพักขณะพยายามทำให้มันเข้าคู่กับสิ่งอื่นไม่ประสบความสำเร็จ
primo

76
มนุษย์จะผลิตสิ่งนั้นได้อย่างไร
xem

61
นี่เป็นคำตอบที่ได้รับการโหวตสูงสุดในเว็บไซต์นี้
Cruncher

44
นี่คือสิ่งที่ไร้สาระและเหลือเชื่อที่สุดที่ฉันเคยเห็นมา
Alex A.

22
มีคนทวีตโพสต์นี้ดังนั้นฉันจึงมีผู้
โหวตขึ้น
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.