regexp เมื่อใดที่ไม่ใช่นิพจน์ปกติ


9

ตั้งแต่ผมกำลังเรียนวิชาภาษาอย่างเป็นทางการที่วิทยาลัยของฉันฉัน stumbled เมื่อโพสต์ที่น่าสนใจเหล่านี้ ( หนึ่ง สอง ) ซึ่งอธิบายถึงวิธีการที่จะหาจำนวนที่สำคัญใช้regexp ขณะที่ผมกล่าวว่าregexpไม่แสดงออกปกติ เนื่องจากนิพจน์ทั่วไปสามารถจับคู่สตริงที่คำนวณโดย Automite State Automata และการค้นหาหมายเลขเฉพาะไม่สามารถทำได้โดย FSA, regexp ที่แสดงในบล็อกโพสต์ไม่ได้เป็นนิพจน์ปกติทั้งหมดเนื่องจากมันจะย้อนรอยเพื่อจับคู่สตริง

เนื่องจากฉันไม่เคยใช้นิพจน์ทั่วไปจริงๆตอนนี้คำถามของฉัน:

ฉันจะจำ regexp จากนิพจน์ปกติ "ของจริง" ได้ทันทีโดยดูจากอะไร?

คำจำกัดความ: โดยการแสดงออกปกติฉันหมายถึงความคิดตามที่กำหนดไว้ในภาษาที่เป็นทางการ โดย regexp ฉันหมายถึงความคิดที่สนับสนุนโดยภาษาโปรแกรมสมัยใหม่ ไวยากรณ์ regexp มักจะมีคุณสมบัติเพิ่มเติมเช่น backreferences Regexps ตามที่เห็นในภาษาการเขียนโปรแกรมมีประสิทธิภาพมากกว่าการแสดงออกปกติของสไตล์ทางการ


5
Regexp เป็นเพียงตัวย่อของนิพจน์ทั่วไป การคำนวณจำนวนเฉพาะจะขึ้นอยู่กับการแฮ็ค Perl ไม่ใช่ในการแสดงผลปกติ

1
มันค่อนข้างง่าย ภาษาปกติใช้การต่อข้อมูลซ้ำซ้อนและการสลับ เมื่อใดก็ตามที่เครื่องยนต์รองรับบางสิ่งที่ไม่เทียบเท่าสิ่งเหล่านี้มันไม่ได้เป็นแบบปกติ
Kilian Foth

1
คำถามที่เกี่ยวข้อง: 1 , 2 , 3
Raphael

@ Yanis ถ้าคุณกระโดดข้ามรั้วไปที่ CS นั่นไม่เป็นความจริงอีกต่อไป Regexps ตามที่เห็นในภาษาการเขียนโปรแกรมมีประสิทธิภาพมากกว่าการแสดงออกปกติ (รูปแบบภาษาทางการ) อย่างเข้มงวดและรูปแบบสั้น ๆ "regexp" เป็นแบบแผน ชนิด.
กราฟิลส์

@KilianFoth นั่นไม่ใช่คำอธิบายที่เป็นประโยชน์จริงๆ ตัวอย่างเช่นคุณสามารถเพิ่มการปฏิเสธ (หรือชุดการเชื่อมต่อบูลีน) กับนิพจน์ทั่วไปโดยไม่เพิ่มพลัง
David Richerby

คำตอบ:


13

tl; dr backrefs

ทันทีที่มี\1(หรือหมายเลขใด ๆ ที่ไม่ได้ใช้เพื่อหลบหนี unicode) ใน regexp มันไม่ใช่การแสดงออกปกติ

Backrefs ช่วยให้คุณสามารถจับคู่(a+)b\1ที่ตรงกับ n ครั้งaตามด้วย b ตามด้วย n ครั้งaสำหรับ n> 1 ใด ๆ นี่ไม่ใช่ภาษาปกติ (มันเป็นลูกหลานของภาษาที่ไม่ใช่ภาษาปกติ)

มันเป็นสิ่งจำเป็นและเกือบเพียงพอที่อ้างอิง backref กลุ่มที่มี regexp ที่ตรงกับสายยาวโดยพลการหรือว่ามันมีได้หรือ* +ข้อยกเว้นเพียงอย่างเดียว (ที่ฉันค้นพบ) ของ regexp ของแบบฟอร์ม(A)B\1ที่ A เป็นภาษาที่ จำกัด (อาจถูกแทนที่ด้วยการนับจำนวนคำทั้งหมดที่ยอมรับได้) คุณสามารถแปลงเป็นword1+Bword1|word2+Bword2ฯลฯ ได้เนื่องจาก A จำกัด

กลุ่มที่ดูรอบ ๆ ไม่ได้ลบความสม่ำเสมอของ regexp A(?=B)Cคือ cross-section ของ regexes AB.*และACcross-section ของ 2 ภาษาปกติเป็นปกติ Lookahead เชิงลบคล้ายกันยกเว้นการใช้ส่วนเติมเต็มของB.*(เติมเต็มภาษาปกติเป็นปกติ) Lookbehind เป็นสิ่งเดียวกันเช่นกันA(?<=B)Cคือข้ามส่วนของและAC.*BC


สิ่งนี้จำเป็นและเพียงพอหรือไม่ ดูเหมือนว่าฉันชอบ(a)\1ในขณะที่ใช้ backref จะเทียบเท่าaaและปกติเล็กน้อย ฉันยังสงสัยด้วยว่าการอ้างสิทธิ์ lookahead สามารถใช้เพื่อรับรู้ภาษาที่ไม่ปกติหรือไม่
MSalters

1
@MSalters: หากคุณต้องการได้รับเทคนิคจริงๆ(a)\1ไม่ใช่การแสดงออกปกติ แต่จำภาษาปกติ
Jörg W Mittag
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.