Regex ที่ไม่มีวันจับคู่กับสิ่งใด ๆ


131

นี่อาจฟังดูเป็นคำถามโง่ ๆ แต่ฉันได้คุยกับเพื่อนนักพัฒนาบางคนเป็นเวลานานและฟังดูเหมือนเป็นเรื่องสนุกที่คิด

ดังนั้น; ความคิดของคุณเป็นอย่างไร - Regex มีลักษณะอย่างไรที่จะไม่จับคู่กับสตริงใด ๆ เลย!

แก้ไข : ทำไมฉันถึงต้องการสิ่งนี้? ประการแรกเพราะฉันคิดว่ามันน่าสนใจที่จะนึกถึงการแสดงออกเช่นนี้และประการที่สองเพราะฉันต้องการมันสำหรับสคริปต์

ในสคริปต์นั้นฉันกำหนดพจนานุกรมเป็น Dictionary<string, Regex>ในบทที่ผมกำหนดเป็นพจนานุกรมสิ่งนี้ประกอบด้วยสตริงและนิพจน์ตามที่คุณเห็น

จากพจนานุกรมนั้นฉันสร้างเมธอดที่ทุกคนใช้พจนานุกรมนี้เป็นเพียงข้อมูลอ้างอิงเกี่ยวกับวิธีการทำงานของพวกเขาหนึ่งในนั้นจะจับคู่นิพจน์ทั่วไปกับไฟล์บันทึกที่แยกวิเคราะห์

ถ้านิพจน์ตรงกับนิพจน์อื่น Dictionary<string, long>ถูกเพิ่มค่าที่นิพจน์ส่งกลับ ดังนั้นในการตรวจจับข้อความบันทึกใด ๆ ที่ไม่ตรงกับนิพจน์ในพจนานุกรมฉันจึงสร้างกลุ่มใหม่ชื่อ "ไม่ทราบ"

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

ดังนั้นคุณมีเหตุผลของฉันที่ "ไม่ใช่คำถามจริง" ...


1
โปรดทราบว่ามันยากมากที่จะพิสูจน์ว่าเป็นลบ
Lasse V.Karlsen

5
น่าสนใจ คุณจะใช้นิพจน์ทั่วไปที่ไหน
Charlie Salts

1
ฉันจะบันทึกไว้ที่นี่เพื่อบันทึกว่าความคิดเห็นมากมายข้างต้นและคำตอบสำหรับคำถามนี้มาจากstackoverflow.com/questions/1845078/…ซึ่งเป็นความคิดเห็นที่ฉันถาม Marc Gravell รวมเข้าด้วยกันซึ่งฉันคิดว่าทำให้การตอบสนองเหล่านี้เป็นเรื่องแปลกประหลาดโดยไม่มีบริบทดั้งเดิมที่แม่นยำจนถึงจุดที่ความคิดเห็นบางส่วนดูเหมือนจะไม่สมเหตุสมผล (อาจจะขโมยคะแนนตัวแทนในอนาคตไปด้วย) ฉันขอแนะนำว่าคำถามที่มีภูมิหลังโดยละเอียดเช่นนี้จะไม่มีทาง "ซ้ำกันทุกประการ" ก็ตาม ...
Peter Hansen

2
คำถามนี้ถูกเพิ่มเข้าไปในคำถามที่พบบ่อยเกี่ยวกับนิพจน์ทั่วไปของStack Overflowภายใต้ "Advanced Regex-Fu"
aliteralmind

3
"โปรดทราบว่าเป็นการยากมากที่จะพิสูจน์ว่าเป็นลบ" ซึ่งเป็นที่เชื่อกันอย่างกว้างขวาง แต่เป็นเท็จอย่างเต็มที่และชัดเจน ... และหลักฐานใด ๆ ของ P เป็นหลักฐานการปฏิเสธของ (ไม่ใช่ P) สิ่งที่เป็นความจริงนั้นยากที่จะพิสูจน์ได้ว่าเป็นสากลเชิงประจักษ์เชิงบวกหรือเชิงลบเช่น "กาทั้งหมดเป็นสีดำ" หรือ "กาไม่มีสีขาว" อัลกอริทึมเป็นการวิเคราะห์ไม่ใช่เชิงประจักษ์ดังนั้นนี่จึงเป็นการใช้กฎปลอมในทางที่ผิดโดยเฉพาะ เช่นการพิสูจน์ว่ารูปแบบ "a" ไม่ตรงกับสตริงใด ๆ ที่ขึ้นต้นด้วย "b" ไม่ใช่ "ยากมาก"
Jim Balter

คำตอบ:


66

นี่เป็นเรื่องง่ายมากแม้ว่าจะขึ้นอยู่กับการนำไปใช้งาน / แฟล็ก *:

$a

จะจับคู่อักขระaหลังสิ้นสุดสตริง โชคดี.

คำเตือน:
นิพจน์นี้มีราคาแพง - จะสแกนทั้งบรรทัดค้นหาจุดยึดท้ายบรรทัดจากนั้นจะไม่พบaและส่งคืนการจับคู่เชิงลบ (ดูความคิดเห็นด้านล่างสำหรับรายละเอียดเพิ่มเติม)


*เดิมทีฉันไม่ได้คิดมากเกี่ยวกับ regexp โหมดหลายสายซึ่ง$ตรงกับจุดสิ้นสุดของบรรทัดด้วย ในความเป็นจริงมันจะจับคู่สตริงว่างก่อนขึ้นบรรทัดใหม่ดังนั้นอักขระธรรมดาอย่างaจะไม่ปรากฏหลังจาก$นั้น


50
นิพจน์นี้มีราคาแพง - จะสแกนทั้งบรรทัดค้นหาจุดยึดท้ายบรรทัดจากนั้นจะไม่พบ "a" และส่งคืนการจับคู่เชิงลบ ฉันเห็นว่าใช้เวลาประมาณ 480ms ในการสแกนไฟล์บรรทัด ~ 275k การสนทนา "a ^" ใช้เวลาประมาณเดียวกันแม้ว่าอาจจะดูมีประสิทธิภาพมากกว่าก็ตาม ในทางกลับกันผู้มองเชิงลบไม่จำเป็นต้องสแกนอะไร: "(?! x) x" (สิ่งที่ไม่ตามด้วย x แล้วตามด้วย x คือไม่มีอะไร) ใช้เวลาประมาณ 30 มิลลิวินาทีหรือน้อยกว่า 7% ของเวลา (วัดด้วยเวลา gnu และ egrep)
arantius

1
ใน Perl ที่จะตรงกับค่าปัจจุบันของ$a. มันเทียบเท่ากับ Perl $(?:a)ก็ช้ามากperl -Mre=debug -e'$_=a x 50; /$(?:a)/'เช่นกัน
Brad Gilbert

@arantius โปรดดูคำตอบของฉันเกี่ยวกับระยะเวลาที่ผมคิดว่าตรงข้ามกับวัดและtimeit python3
nivk

ไม่น่าตกใจที่หกปีและ Python เวอร์ชันหลักอาจเปลี่ยนแปลงสิ่งต่างๆ
arantius

1
ในไวยากรณ์ POSIX BRE $aจะจับคู่ข้อความตามตัวอักษร$aเนื่องจาก$ไม่ถูกต้องเป็นจุดยึดในรูปแบบนั้น
Phils

76

เลเวอเรจnegative lookahead:

>>> import re
>>> x=r'(?!x)x'
>>> r=re.compile(x)
>>> r.match('')
>>> r.match('x')
>>> r.match('y')

RE นี้มีความขัดแย้งในแง่และดังนั้นจะไม่ตรงกับสิ่งใด

หมายเหตุ:
ใน Python re.match ()จะเพิ่มจุดยึดเริ่มต้นของสตริงโดยปริยาย ( \A) ไปยังจุดเริ่มต้นของนิพจน์ทั่วไป จุดยึดนี้มีความสำคัญต่อประสิทธิภาพ: หากไม่มีสิ่งนี้ระบบจะสแกนสตริงทั้งหมด ผู้ที่ไม่ได้ใช้ Python จะต้องการเพิ่มจุดยึดอย่างชัดเจน:

\A(?!x)x

@Chris, ใช่ - เช่นกัน(?=x)(?!x)และอื่น ๆ (การเรียงต่อกันของ lookaheads ที่ขัดแย้งกันและเหมือนกันสำหรับ lookbehinds) และหลาย ๆ อย่างก็ใช้ค่าตามอำเภอใจของx(lookbehinds ต้องการxs ที่ตรงกับสตริงที่มีความยาวคงที่)
Alex Martelli

1
ปรากฏว่าใช้งานได้ดี แต่สิ่งที่เกี่ยวกับเพียง (?!) แทน? เนื่องจาก () จะตรงกันเสมอจะไม่รับประกัน (?!) ว่าจะไม่จับคู่?
Peter Hansen

2
@Peter ใช่ถ้า Python ยอมรับไวยากรณ์นั้น (และมีการเผยแพร่ล่าสุด) ก็จะขัดแย้งในตัวเองเช่นกัน อีกแนวคิดหนึ่ง (ไม่สวยหรูเท่าไหร่ แต่ยิ่งมีไอเดียมากเท่าไหร่คุณก็จะพบว่าสิ่งหนึ่งที่ใช้ได้กับเอ็นจิน RE ทั้งหมดที่สนใจ): r'a\bc'มองหาขอบเขตคำที่ล้อมรอบด้วยตัวอักษรทั้งสองด้านทันที (ตัวแปร: อักขระที่ไม่ใช่คำบน ทั้งสองด้าน).
Alex Martelli

1
ที่น่าสนใจคือต้นฉบับของฉันที่มีตัวอักษรธรรมดาที่ฉัน "รู้" จะไม่ปรากฏในข้อมูลที่ฉันป้อนกลับกลายเป็นว่าเร็วที่สุดใน Python ด้วยสตริงอินพุต 5MB และใช้สิ่งนี้ในการดำเนินการย่อย () (?! x) x ใช้เวลานานขึ้น 21% (?! ()) คือ 16% และ ($ ^) ยาวขึ้น 6% อาจมีความสำคัญในบางกรณีแม้ว่าจะไม่ได้อยู่ในเหมืองก็ตาม
Peter Hansen

2
perl -Mre=debug -e'$_=x x 8; /(?!x)x/'ที่สามารถจะค่อนข้างช้า คุณสามารถทำให้มันเร็วขึ้นโดยการยึดมันในตอนต้นหรือตอนท้าย\A(?!x)x (?!x)x\zperl -Mre=debug -e'$_=x x 8; /(?!x)x\z/; /\A(?!x)x/'
Brad Gilbert

43

สิ่งที่พลาด:

^\b$

ไม่สามารถจับคู่ได้เนื่องจากสตริงว่างไม่มีขอบเขตของคำ ทดสอบใน Python 2.5


7
นี่คือคำตอบที่ดีที่สุด ไม่ใช้ lookaheads ไม่ทำลายภายใต้การใช้งาน regex บางตัวไม่ใช้อักขระเฉพาะ (เช่น 'a') และล้มเหลวในขั้นตอนการประมวลผลสูงสุด 3 ขั้นตอน (ตาม regex101.com) โดยไม่ต้องสแกนทั้งหมด สตริงอินพุต นอกจากนี้ยังเข้าใจได้ง่ายในพริบตา
CubicleSoft

1
สิ่งนี้ล้มเหลวใน Emacs ในเงื่อนไขบางประการ (หากมีบรรทัดว่างที่จุดเริ่มต้นหรือจุดสิ้นสุดของบัฟเฟอร์) อย่างไรก็ตามได้\`\b\'ผลซึ่งจะแทนที่ไวยากรณ์ Emacs สำหรับ "จุดเริ่มต้น / จุดสิ้นสุดของข้อความ" (ตรงข้ามกับ "จุดเริ่มต้น / จุดสิ้นสุด ของบรรทัด ")
Phils

35

มองไปรอบ ๆ:

(?=a)b

สำหรับมือใหม่ regex: การมองไปข้างหน้าในเชิงบวก(?=a)จะทำให้แน่ใจว่าอักขระถัดไปคือaแต่ไม่เปลี่ยนตำแหน่งการค้นหา (หรือรวม "a" ในสตริงที่ตรงกัน) ตอนนี้ตัวอักษรต่อไปคือการยืนยันที่จะเป็นaส่วนหนึ่งที่เหลืออยู่ของ regex (ที่b) bตรงกับเฉพาะถ้าตัวอักษรต่อไปคือ ดังนั้น regex นี้จะจับคู่เฉพาะในกรณีที่อักขระเป็นทั้งสองตัวaและbในเวลาเดียวกัน


30

a\bcซึ่ง\bคือการแสดงออกเป็นศูนย์ที่มีความกว้างที่ตรงกับขอบเขตของคำ

มันไม่สามารถปรากฏตรงกลางของคำซึ่งเราบังคับให้ใช้


หากกรณีการใช้งานของคุณอนุญาตให้คุณยึดรูปแบบไว้ที่จุดเริ่มต้นของสตริงการปรับปรุงดังกล่าวจะป้องกันไม่ให้เครื่องมือ regexp ค้นหาและทดสอบอินสแตนซ์ทั้งหมดของaข้อความ
Phils

20

$.

.^

$.^

(?!)


1
น่ารัก! จิตใต้สำนึกของฉันดึงฉันออกไปจากความคิดเช่นสามประการแรกเนื่องจากพวกเขา "ผิดกฎหมาย" ... ตามแนวความคิด แต่เห็นได้ชัดว่าไม่ใช่นิพจน์ทั่วไป ฉันไม่รู้จัก (!) อัน ... จะต้องมองขึ้นไป
Peter Hansen

1
เอาล่ะฉันชอบคำตอบ (?!) ... สิ่งที่อเล็กซ์แนะนำอย่างมีประสิทธิภาพ โปรดทราบว่าในstackoverflow.com/questions/1723182 (ชี้ให้เห็นโดย Amarghosh ด้านบน) มีคนอ้างว่า "รสชาติบางอย่าง" ของ regex จะพิจารณาว่าเป็นข้อผิดพลาดทางไวยากรณ์ Python ชอบมาก โปรดทราบว่าคำแนะนำอื่น ๆ ของคุณทั้งหมดจะล้มเหลวด้วยโหมด re.DOTALL | re.MULTILINE ใน Python
Peter Hansen

1
ได้รับการทดสอบแล้วหรือยัง? ฉันจะเดาได้ว่า^มีความหมายพิเศษเป็นอักขระตัวแรกของ regexp $เท่านั้นและมีความหมายพิเศษที่ส่วนท้ายของ regexp เท่านั้นเว้นแต่นิพจน์ทั่วไปจะเป็นนิพจน์หลายบรรทัด
พีพี.

จริงๆแล้วใน Perl /$./หมายถึงสิ่งที่แตกต่างไปจากเดิมอย่างสิ้นเชิง มันมีความหมายตรงกับค่าปัจจุบันของ(ใส่หมายเลขบรรทัด)$. แม้/$(.)/อาจไม่ตรงกับบางสิ่งบางอย่างถ้าคุณเขียนuse re '/s';ก่อนที่มันจะ ( perl -E'say "\n" =~ /$(.)/s || 0')
Brad Gilbert

ในไวยากรณ์ POSIX BRE, ^และ$เป็นเพียงพิเศษที่เริ่มต้นและสิ้นสุด (ตามลำดับ) ของแบบแผนจึงไม่มี$.หรือ.^หรือ$.^จะทำงาน (?!)เป็นคุณสมบัติ Perl / PCRE ฉันเชื่อ
Phils

13

การจับคู่สูงสุด

a++a

อย่างน้อยหนึ่งaตามด้วยจำนวนเท่าใดก็ได้aโดยไม่มีการย้อนกลับ จากนั้นลองจับคู่อีกaครั้ง

หรือนิพจน์ย่อยอิสระ

นี้จะเทียบเท่ากับการวางในการแสดงออกย่อยอิสระตามมาด้วยอีกa+a

(?>a+)a

10

Perl 5.10 รองรับคำควบคุมพิเศษที่เรียกว่า "กริยา" ซึ่งอยู่ใน(*...)ลำดับ (เปรียบเทียบกับ(?...)ลำดับพิเศษ) ในนั้นรวมถึง(*FAIL)คำกริยาที่ส่งกลับจากนิพจน์ทั่วไปทันที

โปรดทราบว่าคำกริยาจะถูกนำไปใช้ใน PCRE หลังจากนั้นไม่นานดังนั้นคุณสามารถใช้คำกริยาใน PHP หรือภาษาอื่น ๆ โดยใช้ไลบรารี PCRE ได้เช่นกัน (คุณไม่สามารถใช้ Python หรือ Ruby ได้พวกเขาใช้เอนจิ้นของตัวเอง)


เอกสารดังกล่าวที่perldoc.perl.org/perlre.html#%28%2AFAIL%29-%28%2AF%29ระบุว่า "รูปแบบนี้ตรงกับอะไรและล้มเหลวเสมอมันเทียบเท่ากับ (?!) แต่ง่ายกว่า อ่านในความเป็นจริง (?!) ได้รับการปรับให้เหมาะสมเป็น (* FAIL) ภายใน " น่าสนใจเนื่องจาก (?!) เป็นคำตอบที่ "บริสุทธิ์" ที่ชื่นชอบจนถึงตอนนี้ (แม้ว่าจะใช้ไม่ได้ใน Javascript) ขอบคุณ
Peter Hansen

10
\B\b

\bจับคู่ขอบเขตคำ - ตำแหน่งระหว่างตัวอักษรกับตัวอักษรที่ไม่ใช่ตัวอักษร (หรือขอบเขตสตริง)
\Bเป็นส่วนเติมเต็ม - ตรงกับตำแหน่งระหว่างตัวอักษรสองตัวหรือระหว่างตัวอักษรที่ไม่ใช่

พวกเขาไม่สามารถจับคู่ตำแหน่งใด ๆ ร่วมกันได้

ดูสิ่งนี้ด้วย:


นี่ดูเหมือนจะเป็นวิธีแก้ปัญหาที่ยอดเยี่ยมหากมีการยึดจุดใดจุดหนึ่ง (จุดเริ่มต้นของข้อความจะดูสมเหตุสมผล) หากคุณไม่ทำเช่นนั้นมันเป็นทางออกที่แย่มากเพราะทุกขอบเขตที่ไม่ใช่คำในข้อความจะถูกทดสอบเพื่อดูว่าตามด้วยขอบเขตคำหรือไม่! ^\B\bเพื่อให้รุ่นที่เหมาะสมจะเป็นสิ่งที่ชอบ ในภาษาที่ "จุดเริ่มต้นของข้อความ" และ "จุดเริ่มต้นของบรรทัด" มีไวยากรณ์ที่แตกต่างกันคุณจะต้องใช้ไวยากรณ์ "จุดเริ่มต้นของข้อความ" มิฉะนั้นคุณจะทดสอบทุกบรรทัด (เช่นใน Emacs นี้จะเป็น\`\B\bหรือ"\\`\\B\\b".)
Phils

ที่กล่าวว่าตอนนี้ฉันตั้งข้อสังเกตแล้วว่าจุดประสงค์ที่ระบุไว้ของคำถามนี้คือการได้รับ regexp เพื่อใช้ในกลุ่มซึ่งในกรณีนี้^เป็นปัญหาในไวยากรณ์ regexp บางตัว (เช่น POSIX BRE) ซึ่ง^เป็นเพียงจุดยึดเมื่อเป็นอักขระตัวแรก ของรูปแบบและตรงกับ^อักขระตามตัวอักษร
Phils

@phils - ฉันคิดว่าคุณคิดมากเกินไป:)- นี่เป็นคำถามที่ไม่สามารถใช้งานได้จริงโดยมีเป้าหมายเพื่อหาคำตอบที่น่าสนใจไม่ใช่คำตอบที่มีประสิทธิภาพ ที่กล่าวว่ารูปแบบสามารถปฏิเสธได้ในเวลาซับ (ด้วยขนาดของสตริงเป้าหมาย) ดังนั้นจึงไม่เลวสำหรับ regex - รูปแบบส่วนใหญ่ที่นี่จะเหมือนกันและ^อาจเป็นเส้นตรงหากไม่ได้รับการปรับให้เหมาะสม
Kobi

Re: optimisations, ฉันยินดีที่จะละเว้นเครื่องยนต์ regexp ซึ่งหวังที่จะหา "จุดเริ่มต้นของข้อความ" ที่ตำแหน่งอื่น ๆ :)
Phils

นอกจากนี้ไม่ใช่การถามตอบที่ใช้ไม่ได้จริง - เหตุผลเดียวที่ฉันลงเอยที่นี่คือเพื่อดูว่ามีใครสามารถแนะนำวิธีแก้ปัญหาที่มีประสิทธิภาพมากกว่าสำหรับตัวฉันเองเพื่อจุดประสงค์ในทางปฏิบัติในการกำหนดค่าตัวแปร Emacs ซึ่งต้องการค่า regexp แต่ฉัน ต้องการปิดการใช้งานอย่างมีประสิทธิภาพ
Phils

8

ดูเหมือนว่าจะได้ผล:

$.

2
นั่นคล้ายกับตัวอย่างของเฟอร์ดินานด์เบเยอร์
Gumbo

9
และจะจับคู่ในโหมด dot-match-newlines
Tim Pietzcker

ใน Perl $.ที่จริงจะตรงกับหมายเลขบรรทัดป้อนข้อมูลปัจจุบัน ในกรณีที่คุณต้องหันไปหรือมากกว่าเท่า$(.) $(?:.)
Brad Gilbert

ในไวยากรณ์ POSIX BRE $.จะจับคู่ตัวอักษร$ตามด้วยอักขระใด ๆ เนื่องจาก$ไม่ถูกต้องเป็นจุดยึดในรูปแบบนั้น
Phils

8

ประมาณ$^หรืออาจจะ(?!)


3
การแบ่งบรรทัดจะจับคู่โดยนิพจน์นี้ในโหมดที่^ตรงกับจุดเริ่มต้นและ$จุดสิ้นสุดของบรรทัด
Gumbo

4
บางทีเขาอาจหมายถึง(?!)- ผู้มองเชิงลบสำหรับสตริงว่าง แต่รสชาติ regex บางอย่างจะถือว่าเป็นข้อผิดพลาดทางไวยากรณ์เช่นกัน
Alan Moore

1
สตริงว่างตรงกับสตริงแรกอย่างน้อยที่สุดใน JavaScript
Roland Pihlakas

ใน POSIX BRE ไวยากรณ์$^จะตรงกับตัวอักษรตัวอักษรเหล่านั้นเพราะตัวอักษรที่ไม่ถูกต้องเป็นแองเคอ (คือเหตุผลมากที่คุณใช้รูปแบบทำให้มันไม่ทำสิ่งที่คุณต้องการ.)
Phils

5

เร็วที่สุดคือ:

r = re.compile(r'a^')
r.match('whatever')

'a' สามารถเป็นอักขระที่ไม่ใช่อักขระพิเศษ ('x', 'y') การใช้งานของ Knio อาจจะบริสุทธิ์กว่าเล็กน้อย แต่อันนี้จะเร็วกว่าสำหรับสตริงทั้งหมดที่ไม่ได้ขึ้นต้นด้วยอักขระใด ๆ ที่คุณเลือกแทนที่จะเป็น 'a' เพราะจะไม่จับคู่หลังอักขระตัวแรกแทนที่จะเป็นตัวที่สองในกรณีเหล่านั้น


อันที่จริง (. ^) จะช้ากว่า (\ x00 ^) ประมาณ 10% ในกรณีของฉัน
Peter Hansen

1
ฉันยอมรับสิ่งนี้เนื่องจากใช้ค่าใด ๆ ที่ไม่ใช่ \ n เนื่องจากตัวอักษรได้รับการรับรองว่าจะไม่ตรงกันและฉันเห็นว่ามันอ่านง่ายกว่าเล็กน้อย (เนื่องจากมีคนเพียงไม่กี่คนที่เป็นผู้เชี่ยวชาญด้าน regex) มากกว่าตัวเลือก (?! x) x แม้ว่าฉันจะโหวตอันนั้นด้วย ในกรณีของฉันสำหรับตัวเลือกใดตัวเลือกหนึ่งฉันต้องการความคิดเห็นเพื่ออธิบายดังนั้นฉันคิดว่าฉันจะปรับความพยายามเดิมของฉันเป็น '\ x00NEVERMATCHES ^' ฉันได้รับการรับประกันที่ไม่ตรงกับคำตอบนี้พร้อมเอกสารต้นฉบับของฉันเอง ขอบคุณทุกคนสำหรับคำตอบ!
Peter Hansen

3
สิ่งนี้ใช้งานได้จริงหรือไม่และถ้าเป็นเช่นนั้นใครตัดสินใจเลิกใช้ Unix? ใน Unix regexps ^มีความพิเศษเป็นอักขระตัวแรกเท่านั้นและในทำนองเดียวกันกับ$. ด้วยเครื่องมือที่ใช้ระบบปฏิบัติการยูนิกซ์ใด ๆ regexp a^ที่จะแข่งขันอะไรที่มีสตริงตัวอักษร
JaakkoK

นี่เป็นการโจมตีที่ดี ฉันไม่เคยทดสอบกับสตริงตัวอักษรนั้น
Adam Nelson

โอ้ถ้าแบ่งว่า Unix regexps >^แล้วคุณจะรัก
CubicleSoft

4

Python ไม่ยอมรับ แต่ Perl จะ:

perl -ne 'print if /(w\1w)/'

นิพจน์ทั่วไปนี้ (ในทางทฤษฎี) ควรพยายามจับคู่จำนวนws ที่ไม่มีที่สิ้นสุด (คู่) เนื่องจากกลุ่มแรกเกิด()ซ้ำในตัวเอง ดูเหมือนว่า Perl จะไม่ออกคำเตือนใด ๆ แม้ว่าจะอยู่ภายใต้use strict; use warnings;ก็ตามดังนั้นฉันถือว่ามันถูกต้องเป็นอย่างน้อยและการทดสอบ (ขั้นต่ำ) ของฉันไม่ตรงกับสิ่งใด ๆ ดังนั้นฉันจึงส่งคำเตือนไปให้คุณวิจารณ์


1
ทฤษฎีเป็นสิ่งที่ดีเสมอ แต่ในทางปฏิบัติฉันคิดว่าฉันคงกังวลเกี่ยวกับนิพจน์ทั่วไปที่มีคำอธิบายรวมถึงคำว่า "ไม่มีที่สิ้นสุด"!
Peter Hansen

perl -Mre=debug -e'"www wwww wwwww wwwwww" =~ /(w\1w)/'
Brad Gilbert

@BradGilbert - การรันที่นี่ (5.10 ล้าสมัยไปหน่อย) จะสร้าง "regex failed" ตามที่ OP ร้องขอ ตรงกับระบบของคุณหรือไม่?
Chris Lutz

4

[^\d\D]หรือ(?=a)bหรือa$aหรือa^a


ขอบคุณ โปรดทราบว่า (?! x) x เป็นคำตอบแรกที่ระบุไว้ด้านบน
Peter Hansen

ใช่ดูเหมือนว่าฉันจะสแกนผู้ตอบคนอื่นเร็วเกินไป
Bart Kiers

4

สิ่งนี้ใช้ไม่ได้กับ Python และภาษาอื่น ๆ อีกมากมาย แต่ใน Javascript regex []เป็นคลาสอักขระที่ถูกต้องซึ่งไม่สามารถจับคู่ได้ ดังนั้นสิ่งต่อไปนี้ควรล้มเหลวทันทีไม่ว่าอินพุตจะเป็นอย่างไร:

var noMatch = /^[]/;

ฉันชอบมันมากกว่า/$a/เพราะว่าสำหรับฉันมันสื่อถึงเจตนาของมันอย่างชัดเจน และสำหรับเวลาที่คุณต้องการฉันต้องการเพราะฉันต้องการทางเลือกสำหรับรูปแบบที่คอมไพล์แบบไดนามิกตามข้อมูลที่ผู้ใช้ป้อน เมื่อรูปแบบไม่ถูกต้องฉันต้องแทนที่ด้วยรูปแบบที่ไม่ตรงกับอะไร ตัวย่อมีลักษณะดังนี้:

try {
    var matchPattern = new RegExp(someUserInput);
}
catch (e) {
    matchPattern = noMatch;
}

4

ตัวอย่างทั้งหมดที่เกี่ยวข้องกับการจับคู่ขอบเขตเป็นไปตามสูตรเดียวกัน สูตรอาหาร:

  1. ใช้ตัวจับคู่ขอบเขตใดก็ได้: ^, $, \ b, \ A, \ Z, \ z

  2. ทำตรงข้ามกับสิ่งที่ตั้งใจไว้

ตัวอย่าง:

^ และ \ A มีไว้สำหรับการเริ่มต้นดังนั้นอย่าใช้ตั้งแต่ต้น

^ --> .^
\A --> .\A

\ b ตรงกับขอบเขตของคำดังนั้นให้ใช้ระหว่าง

\b --> .\b.

$, \ Z และ \ z มีไว้สำหรับการสิ้นสุดดังนั้นอย่าใช้ในตอนท้าย

$ --> $.
\Z --> \Z.
\z --> \z.

คนอื่น ๆ เกี่ยวข้องกับการใช้ lookahead และ lookbehind ซึ่งใช้ได้กับการเปรียบเทียบแบบเดียวกัน: ถ้าคุณให้ lookahead ในเชิงบวกหรือเชิงลบตามด้วยสิ่งที่ตรงกันข้าม

(?=x)[^x]
(?!x)x

หากคุณมองไปในทางบวกหรือลบตามหลังสิ่งที่ตรงกันข้าม

[^x](?<=x)
x(?<!x)

รูปแบบของพวกเขาอาจเป็นรูปแบบดังกล่าวมากกว่าและการเปรียบเทียบเช่นนั้นมากขึ้น


3

คำตอบที่ดีมากมาย!

คล้ายกับคำตอบของ @ nivk ฉันต้องการแบ่งปันการเปรียบเทียบประสิทธิภาพสำหรับ Perl สำหรับรูปแบบต่างๆของ regex ที่ไม่ตรงกัน

  1. อินพุต: สตริง ascii สุ่มหลอก (ต่างกัน 25,000 บรรทัดความยาว 8-16):

ความเร็ว Regex:

Total for   \A(?!x)x: 69.675450 s, 1435225 lines/s
Total for       a\bc: 71.164469 s, 1405195 lines/s
Total for    (?>a+)a: 71.218324 s, 1404133 lines/s
Total for       a++a: 71.331362 s, 1401907 lines/s
Total for         $a: 72.567302 s, 1378031 lines/s
Total for     (?=a)b: 72.842308 s, 1372828 lines/s
Total for     (?!x)x: 72.948911 s, 1370822 lines/s
Total for       ^\b$: 79.417197 s, 1259173 lines/s
Total for         $.: 88.727839 s, 1127041 lines/s
Total for       (?!): 111.272815 s, 898692 lines/s
Total for         .^: 115.298849 s, 867311 lines/s
Total for    (*FAIL): 350.409864 s, 285380 lines/s
  1. อินพุต: / usr / share / dict / words (100,000 คำในภาษาอังกฤษ)

ความเร็ว Regex:

Total for   \A(?!x)x: 128.336729 s, 1564805 lines/s
Total for     (?!x)x: 132.138544 s, 1519783 lines/s
Total for       a++a: 133.144501 s, 1508301 lines/s
Total for    (?>a+)a: 133.394062 s, 1505479 lines/s
Total for       a\bc: 134.643127 s, 1491513 lines/s
Total for     (?=a)b: 137.877110 s, 1456528 lines/s
Total for         $a: 152.215523 s, 1319326 lines/s
Total for       ^\b$: 153.727954 s, 1306346 lines/s
Total for         $.: 170.780654 s, 1175906 lines/s
Total for       (?!): 209.800379 s, 957205 lines/s
Total for         .^: 217.943800 s, 921439 lines/s
Total for    (*FAIL): 661.598302 s, 303540 lines/s

(Ubuntu บน Intel i5-3320M, Linux kernel 4.13, Perl 5.26)


นี่คือการเปรียบเทียบ JavaScript ของวิธีการบางอย่างที่กล่าวถึงที่นี่: jsperf.com/regex-that-never-matches
thdoan

2

ฉันเชื่ออย่างนั้น

\Z RE FAILS! \A

ครอบคลุมแม้กระทั่งกรณีที่นิพจน์ทั่วไปมีแฟล็กเช่น MULTILINE, DOTALL เป็นต้น

>>> import re
>>> x=re.compile(r"\Z RE FAILS! \A")
>>> x.match('')
>>> x.match(' RE FAILS! ')
>>>

ฉันเชื่อว่า (แต่ฉันยังไม่ได้เปรียบเทียบ) ว่าความยาว (> 0) ของสตริงระหว่าง\Zและ\Aเวลาใดควรจะคงที่


2
(*FAIL)

หรือ

(*F)

ด้วย PCRE และ PERL คุณสามารถใช้คำกริยาควบคุมการย้อนรอยที่บังคับให้รูปแบบล้มเหลวในทันที


2

หลังจากได้เห็นคำตอบที่ยอดเยี่ยมเหล่านี้ความคิดเห็นของ @ arantius (เกี่ยวกับเวลา$xเทียบx^กับ vs (?!x)x) ในคำตอบที่ยอมรับในปัจจุบันทำให้ฉันต้องการเวลาบางส่วนของคำตอบที่ให้ไว้

ด้วยการใช้มาตรฐาน 275k ของ @ arantius ฉันทำการทดสอบต่อไปนี้ใน Python (v3.5.2, IPython 6.2.1)

TL; DR: 'x^'และ'x\by'เร็วที่สุดโดยปัจจัยอย่างน้อย ~ 16 และตรงกันข้ามกับการค้นพบของ @ arantius (?!x)xอยู่ในกลุ่มที่ช้าที่สุด (ช้ากว่า ~ 37 เท่า) ดังนั้นคำถามเกี่ยวกับความเร็วจึงขึ้นอยู่กับการใช้งานอย่างแน่นอน ทดสอบด้วยตัวคุณเองในระบบที่คุณต้องการก่อนตัดสินใจว่าความเร็วมีความสำคัญสำหรับคุณหรือไม่

UPDATE:มีความแตกต่างที่เห็นได้ชัดขนาดใหญ่ระหว่างระยะเวลาและ'x^' 'a^'โปรดดูคำถามนี้สำหรับข้อมูลเพิ่มเติมและแก้ไขก่อนหน้านี้สำหรับการกำหนดเวลาช้าลงด้วยแทนax

In [1]: import re

In [2]: with open('/tmp/longfile.txt') as f:
   ...:     longfile = f.read()
   ...:     

In [3]: len(re.findall('\n',longfile))
Out[3]: 275000

In [4]: len(longfile)
Out[4]: 24733175

In [5]: for regex in ('x^','.^','$x','$.','$x^','$.^','$^','(?!x)x','(?!)','(?=x)y','(?=x)(?!x)',r'x\by',r'x\bx',r'^\b$'
    ...: ,r'\B\b',r'\ZNEVERMATCH\A',r'\Z\A'):
    ...:     print('-'*72)
    ...:     print(regex)
    ...:     %timeit re.search(regex,longfile)
    ...:     
------------------------------------------------------------------------
x^
6.98 ms ± 58.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
------------------------------------------------------------------------
.^
155 ms ± 960 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
$x
111 ms ± 2.12 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
$.
111 ms ± 1.76 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
$x^
112 ms ± 1.14 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
$.^
113 ms ± 1.44 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
$^
111 ms ± 839 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
(?!x)x
257 ms ± 5.03 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
------------------------------------------------------------------------
(?!)
203 ms ± 1.56 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
(?=x)y
204 ms ± 4.84 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
------------------------------------------------------------------------
(?=x)(?!x)
210 ms ± 1.66 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
------------------------------------------------------------------------
x\by
7.41 ms ± 122 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
------------------------------------------------------------------------
x\bx
7.42 ms ± 110 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
------------------------------------------------------------------------
^\b$
108 ms ± 1.05 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
\B\b
387 ms ± 5.77 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
------------------------------------------------------------------------
\ZNEVERMATCH\A
112 ms ± 1.52 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
\Z\A
112 ms ± 1.38 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

ครั้งแรกที่ฉันเรียกใช้สิ่งนี้ฉันลืมที่จะraw 3 นิพจน์สุดท้ายดังนั้นจึง'\b'ถูกตีความว่าเป็น'\x08'อักขระ backspace อย่างไรก็ตามสิ่งที่ฉันประหลาดใจ'a\x08c'คือเร็วกว่าผลลัพธ์ที่เร็วที่สุดก่อนหน้านี้! เพื่อความเป็นธรรมมันจะยังคงตรงกับข้อความนั้น แต่ฉันคิดว่ามันยังน่าสังเกตเพราะฉันไม่แน่ใจว่าทำไมมันเร็วกว่า

In [6]: for regex in ('x\by','x\bx','^\b$','\B\b'):
    ...:     print('-'*72)
    ...:     print(regex, repr(regex))
    ...:     %timeit re.search(regex,longfile)
    ...:     print(re.search(regex,longfile))
    ...:     
------------------------------------------------------------------------
y 'x\x08y'
5.32 ms ± 46.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
None
------------------------------------------------------------------------
x 'x\x08x'
5.34 ms ± 66.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
None
------------------------------------------------------------------------
$ '^\x08$'
122 ms ± 1.05 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
None
------------------------------------------------------------------------
\ '\\B\x08'
300 ms ± 4.11 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
None

ไฟล์ทดสอบของฉันสร้างขึ้นโดยใช้สูตรสำหรับ"... เนื้อหาที่อ่านได้และไม่มีเส้นซ้ำ" (บน Ubuntu 16.04):

$ ruby -e 'a=STDIN.readlines;275000.times do;b=[];rand(20).times do; b << a[rand(a.size)].chomp end; puts b.join(" "); end' < /usr/share/dict/words > /tmp/longfile.txt

$ head -n5 /tmp/longfile.txt 
unavailable speedometer's garbling Zambia subcontracted fullbacks Belmont mantra's
pizzicatos carotids bitch Hernandez renovate leopard Knuth coarsen
Ramada flu occupies drippings peaces siroccos Bartók upside twiggier configurable perpetuates tapering pint paralyzed
vibraphone stoppered weirdest dispute clergy's getup perusal fork
nighties resurgence chafe

\B\bมีข้อบกพร่องอย่างมากในด้านประสิทธิภาพ (เช่นเดียวกับทุกรูปแบบที่ไม่ได้ยึดกับตำแหน่ง แต่รูปแบบนี้ไม่ดีเป็นพิเศษ) ลองเปรียบเทียบ^\B\bแทน
Phils

2

regex ว่างเปล่า

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

regex ที่เป็นไปไม่ได้

อีกวิธีหนึ่งคือการสร้างนิพจน์ทั่วไปที่เป็นไปไม่ได้ ฉันพบว่า$-^ใช้เวลาเพียงสองขั้นตอนในการคำนวณโดยไม่คำนึงถึงขนาดของข้อความของคุณ ( https://regex101.com/r/yjcs1Z/1 )

สำหรับการอ้างอิง:

  • $^และ$.ทำ 36 ขั้นตอนในการคำนวณ -> O (1)
  • \b\B ใช้เวลา 1507 ขั้นตอนในตัวอย่างของฉันและเพิ่มขึ้นตามจำนวนอักขระในสตริงของคุณ -> O (n)

กระทู้ยอดนิยมเพิ่มเติมเกี่ยวกับคำถามนี้:


1

อาจจะเป็นแบบนี้?

/$.+^/

ใน Python วิธีนี้ใช้ได้เฉพาะเมื่อคุณควบคุมแฟล็ก : re.compile('$.+^', re.MULTILINE|re.DOTALL).search('a\nb\nc\n')ส่งคืนอ็อบเจ็กต์การจับคู่ที่สอดคล้องกับ b และ c (และทั้งหมดที่อยู่ติดกันและอยู่ระหว่างขึ้นบรรทัดใหม่) แนวทางเชิงลบที่ฉันแนะนำให้ใช้งานได้ (กล่าวคือไม่สามารถจับคู่สิ่งใด ๆ ) สำหรับการรวมกันของแฟล็กที่สามารถคอมไพล์ด้วย
Alex Martelli

ฉันไม่ดี - ผสม$และ^.
Chris Lutz

1
นี่อาจเป็นความพยายามที่จะมองหาจุดสิ้นสุดของสตริงก่อนจุดเริ่มต้น แต่ฉันพบว่า $ ไม่ได้หมายถึง 'จุดสิ้นสุดของสตริง' เว้นแต่จะเป็นอักขระตัวสุดท้ายของ regex และฉันคาดว่าจะมีพฤติกรรมที่คล้ายกัน ถึง ^ ดังนั้นสิ่งนี้อาจจับคู่สตริงย่อยที่เริ่มต้นด้วย $ ตามตัวอักษรและลงท้ายด้วยตัวอักษร ^
pavium

@pavium แน่นอนว่ามันไม่ทำงานใน Python หรือ Javascript เว้นแต่คุณจะหลีกเลี่ยงด้วย \ หรือรวมไว้ในชุดอักขระที่มี [] อักขระพิเศษเช่น $ และ ^ ไม่ควรถือเป็นตัวอักษร คุณสังเกตสิ่งนี้ในภาษาใด
Peter Hansen

ใน Perl อย่างน้อยควรเขียน/\z.+\A/(ดูperldoc perlre ) ที่ป้องกันไม่ให้use re '/ms'มีผลต่อโหมดหลายบรรทัดและบรรทัดเดียว ( )
Brad Gilbert

0
'[^0-9a-zA-Z...]*'

และแทนที่ ... ด้วยสัญลักษณ์ที่พิมพ์ได้ทั้งหมด;) สำหรับไฟล์ข้อความ


ฉันคิดว่ามันต้องมีวิธีที่สั้นกว่านี้แน่ ๆ แต่นั่นก็เป็นความคิดแรกของฉันเหมือนกัน ^^
FP

4
สิ่งนี้จะตรงกับสตริงว่าง ในการจับอักขระที่เป็นไปได้ทุกตัวให้ใช้[^\x00-\xFF]+(สำหรับการใช้งานแบบไบต์)
Ferdinand Beyer

6
[^\s\S]แสดงออกที่ดีกว่าจะ แต่อย่างที่เฟอร์ดินานด์เบเยอร์พูดไปแล้วมันจะจับคู่สตริงว่างเปล่า
Gumbo

3
regex ของ Drakosha สามารถจับคู่สตริงว่างได้เนื่องจาก*; ปล่อยทิ้งไว้หรือแทนที่ด้วย+และต้องตรงกับอักขระอย่างน้อยหนึ่งตัว หากคลาสไม่รวมอักขระที่เป็นไปได้ทั้งหมดจะไม่สามารถจับคู่อะไรได้
Alan Moore

0

แล้วแทนที่จะใช้ regex ให้ใช้คำสั่ง if ที่เป็นเท็จเสมอ? ในจาวาสคริปต์:

var willAlwaysFalse=false;
if(willAlwaysFalse)
{
}
else
{
}

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

-2

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

cat /dev/urandom | hexdump | head -20
0000000 5d5d 3607 40d8 d7ab ce72 aae1 4eb3 ae47
0000010 c5e2 b9e8 910d a2d9 2eb3 fdff 6301 c85f
0000020 35d4 c282 e439 33d8 1c73 ca78 1e4d a569
0000030 8aca eb3c cbe4 aff7 d079 ca38 8831 15a5
0000040 818b 323f 0b02 caec f17f 387b 3995 88da
0000050 7b02 c80b 2d42 8087 9758 f56f b71f 0053
0000060 1501 35c9 0965 2c6e 03fe 7c6d f0ca e547
0000070 aba0 d5b6 c1d9 9bb2 fcd1 5ec7 ee9d 9963
0000080 6f0a 2c91 39c2 3587 c060 faa7 4ea4 1efd
0000090 6738 1a4c 3037 ed28 f62f 20fa 3d57 3cc0
00000a0 34f0 4bc2 3067 a1f7 9a87 086b 2876 1072
00000b0 d9e1 6b8f 5432 a60e f0f5 00b5 d9ef ed6f
00000c0 4a85 70ee 5ec4 a378 7786 927f f126 2ec2
00000d0 18c5 46fe b167 1ae6 c87c 1497 48c9 3c09
00000e0 8d09 e945 13ce 7da2 08af 1a96 c24c c022
00000f0 b051 98b3 2bf5 4d7d 5ec4 e016 a50d 355b
0000100 0e89 d9dd b153 9f0e 9a42 a51f 2d46 2435
0000110 ef35 17c2 d2aa 3cc7 e2c3 e711 d229 f108
0000120 324e 5d6a 650a d151 bc55 963f 41d3 66ee
0000130 1d8c 1fb1 1137 29b2 abf7 3af7 51fe 3cf4

แน่นอนว่านี้ไม่ได้เป็นความท้าทายทางปัญญา แต่มากขึ้นเช่นการเขียนโปรแกรมเทปพันสายไฟ


-6
new Regex(Guid.NewGuid().ToString())

สร้างรูปแบบที่มีเฉพาะตัวอักษรและตัวเลขคละกันและ ' -' (ไม่มีอักขระใดเป็นอักขระพิเศษ regex) แต่เป็นไปไม่ได้ทางสถิติที่สตริงเดียวกันจะปรากฏที่ใดก็ได้ก่อนหน้านี้ (เพราะนั่นคือจุดรวมของ GUID)


2
"เป็นไปไม่ได้ในทางสถิติ"? ฮะ? ขึ้นอยู่กับวิธีคำนวณ GUID เป็นไปได้และมักจะค่อนข้างง่ายในการทำนาย GUID ถัดไป (เนื่องจากขึ้นอยู่กับเครื่องที่คำนวณและเวลา) คุณหมายถึง "ไม่น่าเป็นไปได้" "ที่มีความน่าจะเป็นน้อยมาก" แต่คุณไม่สามารถพูดว่า "เป็นไปไม่ได้" แม้จะเป็นสตริงสุ่มที่สมบูรณ์แบบก็ตาม Regex ของคุณจะจับคู่สตริงจำนวนไม่ จำกัด - คำถามนี้กำลังมองหาสตริงที่ไม่ตรงกับอะไรเลย เคย.
Ferdinand Beyer
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.