มีการแสดงออกปกติเพื่อตรวจสอบการแสดงออกปกติที่ถูกต้อง?


1006

เป็นไปได้หรือไม่ที่จะตรวจพบนิพจน์ปกติที่ถูกต้องกับนิพจน์ทั่วไปอื่น ถ้าเป็นเช่นนั้นโปรดให้รหัสตัวอย่างด้านล่าง


58
ดังนั้นปัญหาของคุณคือการตรวจสอบ regex คุณเลือก regex สำหรับการแก้ปัญหา ฉันสงสัยว่าคุณสมบัติที่เพิ่มจำนวนปัญหาของ regexes เป็นสารเติมแต่งหรือทวีคูณ มันรู้สึกเหมือน 4 ปัญหาแทน 2 :)
abesto

15
มีสัญลักษณ์หลายอย่างสำหรับการแสดงออกปกติ - คุณลักษณะบางอย่างและการสะกดของพวกเขาเป็นเรื่องธรรมดาสำหรับบางคนมีการสะกดที่แตกต่างกันหรือมีเฉพาะในสัญกรณ์หนึ่งเท่านั้น สัญกรณ์เหล่านั้นส่วนใหญ่ไม่ได้ "ปกติ" ในความหมายของไวยากรณ์ปกติ - คุณต้องมีเครื่องมือแยกวิเคราะห์บริบทเพื่อจัดการกับการซ้อนย่อยของนิพจน์ย่อย - แม้ว่าสัญกรณ์ "นิพจน์ทั่วไป" ที่ทันสมัยจำนวนมากจะมีส่วนขยายที่นอกเหนือจาก อาจอนุญาตให้จดจำสัญลักษณ์ของตนเอง ไม่ว่าในกรณีใดทำไมไม่ถามไลบรารี regex ของคุณว่า regex แต่ละตัวนั้นถูกต้องหรือไม่
Steve314

1
@Bevacqua ฉันต้องตรวจสอบ regexp ใน XML schema ฉันจะทำได้โดยไม่ต้อง regexp อื่นได้อย่างไร
zenden2k

3
รวบรวม / เรียกใช้ regex (รูปแบบ) เพื่อตรวจสอบภายใต้กลไกการจัดการข้อยกเว้นที่ภาษาของคุณมี ดังนั้นโปรแกรม / คอมไพเลอร์ภาษาของตัวเองจะตรวจสอบมัน (นี่ถือว่าไวยากรณ์พื้นฐานที่ถูกต้องเพื่อให้โปรแกรมทำงาน แต่สามารถรวมอยู่ในการตรวจสอบโดยใช้สิ่งอำนวยความสะดวกภาษาของคุณเพื่อประเมินสตริงสำหรับ regex เป็นรหัส (อาจผิด syntactically) รหัสหรือเช่นนั้น)
zdim

นี่คือคำตอบที่สมบูรณ์แบบสำหรับผู้ใช้งาน python: stackoverflow.com/questions/19630994/…
gianni

คำตอบ:


978
/
^                                             # start of string
(                                             # first group start
  (?:
    (?:[^?+*{}()[\]\\|]+                      # literals and ^, $
     | \\.                                    # escaped characters
     | \[ (?: \^?\\. | \^[^\\] | [^\\^] )     # character classes
          (?: [^\]\\]+ | \\. )* \]
     | \( (?:\?[:=!]|\?<[=!]|\?>)? (?1)?? \)  # parenthesis, with recursive content
     | \(\? (?:R|[+-]?\d+) \)                 # recursive matching
     )
    (?: (?:[?+*]|\{\d+(?:,\d*)?\}) [?+]? )?   # quantifiers
  | \|                                        # alternative
  )*                                          # repeat content
)                                             # end first group
$                                             # end of string
/

นี่คือ regex แบบเรียกซ้ำและไม่ได้รับการสนับสนุนจากเอนจิน regex จำนวนมาก คนที่ใช้ PCRE ควรสนับสนุน

ไม่มีช่องว่างและความคิดเห็น:

/^((?:(?:[^?+*{}()[\]\\|]+|\\.|\[(?:\^?\\.|\^[^\\]|[^\\^])(?:[^\]\\]+|\\.)*\]|\((?:\?[:=!]|\?<[=!]|\?>)?(?1)??\)|\(\?(?:R|[+-]?\d+)\))(?:(?:[?+*]|\{\d+(?:,\d*)?\})[?+]?)?|\|)*)$/

.NET ไม่สนับสนุนการสอบถามซ้ำโดยตรง ( (?1)และ(?R)สร้างขึ้น) การเรียกซ้ำจะต้องถูกแปลงเป็นกลุ่มที่มีการนับที่สมดุล:

^                                         # start of string
(?:
  (?: [^?+*{}()[\]\\|]+                   # literals and ^, $
   | \\.                                  # escaped characters
   | \[ (?: \^?\\. | \^[^\\] | [^\\^] )   # character classes
        (?: [^\]\\]+ | \\. )* \]
   | \( (?:\?[:=!]
         | \?<[=!]
         | \?>
         | \?<[^\W\d]\w*>
         | \?'[^\W\d]\w*'
         )?                               # opening of group
     (?<N>)                               #   increment counter
   | \)                                   # closing of group
     (?<-N>)                              #   decrement counter
   )
  (?: (?:[?+*]|\{\d+(?:,\d*)?\}) [?+]? )? # quantifiers
| \|                                      # alternative
)*                                        # repeat content
$                                         # end of string
(?(N)(?!))                                # fail if counter is non-zero.

อัด:

^(?:(?:[^?+*{}()[\]\\|]+|\\.|\[(?:\^?\\.|\^[^\\]|[^\\^])(?:[^\]\\]+|\\.)*\]|\((?:\?[:=!]|\?<[=!]|\?>|\?<[^\W\d]\w*>|\?'[^\W\d]\w*')?(?<N>)|\)(?<-N>))(?:(?:[?+*]|\{\d+(?:,\d*)?\})[?+]?)?|\|)*$(?(N)(?!))

จากความคิดเห็นที่:

สิ่งนี้จะตรวจสอบการแทนที่และการแปลหรือไม่

มันจะตรวจสอบเพียงส่วน regex ของการทดแทนและการแปล s/<this part>/.../

เป็นไปไม่ได้ในทางทฤษฎีที่จะจับคู่ไวยากรณ์ที่ถูกต้องทั้งหมดกับ regex

เป็นไปได้ถ้าเอ็นจิน regex รองรับการเรียกซ้ำเช่น PCRE แต่จะไม่สามารถเรียกนิพจน์ทั่วไปได้อีกต่อไป

แท้จริงแล้ว "นิพจน์ทั่วไปแบบเรียกซ้ำ" ไม่ใช่นิพจน์ทั่วไป แต่นี่เป็นส่วนขยายที่ได้รับการยอมรับบ่อยๆสำหรับเครื่องมือ regex ... โดยปกติแล้ว regex ที่ขยายนี้ไม่ตรงกับ regex ที่ขยายเพิ่ม

“ ในทางทฤษฎีทฤษฏีและการปฏิบัติเหมือนกันในทางปฏิบัติพวกเขาไม่ได้” เกือบทุกคนที่รู้ว่านิพจน์ทั่วไปรู้ว่านิพจน์ทั่วไปไม่รองรับการเรียกซ้ำ แต่ PCRE และการใช้งานอื่น ๆ ส่วนใหญ่รองรับมากกว่าการแสดงออกปกติพื้นฐาน

การใช้สิ่งนี้กับเชลล์สคริปต์ในคำสั่ง grep มันแสดงให้ฉันเห็นข้อผิดพลาดบางอย่าง .. grep: เนื้อหาไม่ถูกต้องของ {} ฉันกำลังสร้างสคริปต์ที่สามารถ grep ฐานรหัสเพื่อค้นหาไฟล์ทั้งหมดที่มีการแสดงออกปกติ

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

Regex เอง "ไม่ใช่ภาษาปกติดังนั้นจึงไม่สามารถแยกวิเคราะห์ด้วยนิพจน์ทั่วไป ... "

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

[]()/\ผมเห็นที่คุณกำลังจับคู่ และอักขระพิเศษ regex อื่น ๆ คุณอนุญาตตัวละครที่ไม่ใช่พิเศษอยู่ที่ไหน ดูเหมือนว่านี้จะตรงแต่ไม่^(?:[\.]+)$ ^abcdefg$นั่นคือ regex ที่ถูกต้อง

[^?+*{}()[\]\\|]จะจับคู่อักขระเดี่ยวใด ๆ ไม่ใช่ส่วนหนึ่งของโครงสร้างอื่น นี้รวมทั้งตัวอักษร ( a- z) และอักขระพิเศษบางอย่าง ( ^, $, .)


10
คำตอบนี้ส่งคนในทิศทางที่ผิดอย่างสมบูรณ์ ไม่ควรใช้ regEx เพื่อค้นหานิพจน์ทั่วไปเนื่องจากไม่สามารถทำงานได้อย่างถูกต้องในทุกกรณี ดูคำตอบของฉันเพิ่ม
vitaly-t

1
.{,1}ไม่มีที่เปรียบ เปลี่ยนเป็นการ^((?:(?:[^?+*{}()[\]\\|]+|\\.|\[(?:\^?\\.|\^[^\\]|[^\\^])(?:[^\]\\]+|\\.)*\]|\((?:\?[:=!]|\?<[=!]|\?>)?(?1)??\)|\(\?(?:R|[+-]?\d+)\))(?:(?:[?+*]|\{\d*(?:,\d*)?\})[?+]?)?|\|)*)$แข่งขัน เปลี่ยน\d+เป็น\d*
yunzen

4
regex โดย def ไม่ควรมี recursion อย่างน้อยก็พูดอย่างนั้นในคำตอบของคุณเครื่องยนต์ ur regex น่าจะ "มีประสิทธิภาพมาก" และไม่ใช่ regex engine จริงๆ
Charlie Parker

แค่ทราบว่าคุณลืมธง x
RedClover

ตัวตรวจสอบความถูกต้องนี้น่าจะใช้กับนิพจน์ PCRE แต่จะผ่าน POSIX ERE ที่ไม่ถูกต้องจำนวนมาก โดยเฉพาะอย่างยิ่งพวกเขาเป็น pickier บิตในช่วงที่ตัวละครคลาสเช่นนี้คือการที่ถูกต้องใน PCRE แต่ไม่ได้อยู่ใน [a-b-c]ERE:
Pedro Gimeno


228

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

มีข้อ จำกัด อย่างหนึ่งของนิพจน์ทั่วไปซึ่งทำให้ไม่สามารถเขียน regex ที่ตรงกับ regexes ทั้งหมดและเฉพาะได้ คุณไม่สามารถจับคู่การติดตั้งใช้งานเช่นเครื่องมือจัดฟันที่จับคู่ไว้ Regexes ใช้โครงสร้างดังกล่าวมากมายลองมา[]เป็นตัวอย่าง เมื่อใดก็ตามที่มีการ[จะต้องมีการจับคู่ซึ่งเพียงพอที่ง่ายสำหรับนิพจน์ทั่วไป]"\[.*\]"

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

ความสามารถนี้มักเรียกว่าการนับเนื่องจากคุณกำลังนับความลึกของการทำรัง regex ตามคำนิยามไม่มีความสามารถในการนับ


ฉันสิ้นสุดการเขียน " ข้อ จำกัด นิพจน์ปกติ " เกี่ยวกับเรื่องนี้


53

คำถามที่ดี.

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

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

Russ Cox เขียนว่า " การจับคู่นิพจน์ทั่วไปทำได้ง่ายและรวดเร็ว " ซึ่งเป็นบทความที่ยอดเยี่ยมเกี่ยวกับการใช้เอ็นจิ้นของ regex


16

ไม่ถ้าคุณใช้นิพจน์ปกติมาตรฐาน

เหตุผลก็คือคุณไม่สามารถตอบสนองบทแทรกสำหรับภาษาปกติได้ สูบน้ำแทรกระบุว่าเป็นสายที่อยู่ในภาษา "L" คือปกติถ้ามีจำนวน "N" ดังกล่าวว่าหลังจากที่การแบ่งสายออกเป็นสามสตริงx, y, zเช่นว่า|x|>=1 && |xy|<=Nคุณสามารถทำซ้ำyหลายครั้งตามที่คุณต้องการและ Lสตริงทั้งหมดจะยังคงอยู่

ผลของการแทรกบทแทรกคือคุณไม่สามารถมีสตริงปกติในรูปแบบa^Nb^Mc^Nได้นั่นคือสองสตริงย่อยที่มีความยาวเท่ากันคั่นด้วยสตริงอื่น ไม่ว่าคุณจะแยกสตริงดังกล่าวเป็นxอย่างไรyและzคุณไม่สามารถ "ปั๊ม" yโดยไม่ได้รับสตริงที่มีจำนวน "a" และ "c" ที่แตกต่างกันดังนั้นจึงเป็นการออกจากภาษาต้นฉบับ ตัวอย่างเช่นกรณีที่มีวงเล็บเป็นนิพจน์ทั่วไป


5
นั่นไม่ใช่คำอธิบายที่แม่นยำมากของบทแทรก เริ่มแรกเป็นภาษาทั้งหมดที่สามารถทำเป็นปกติหรือไม่ไม่ใช่สายเดียว ประการที่สองมันเป็นสิ่งจำเป็นไม่เพียงพอเงื่อนไขสำหรับความสม่ำเสมอ ในที่สุดสตริงที่มีความยาวเพียงพอเท่านั้นที่สามารถปั๊มได้
darij grinberg

13

แม้ว่ามันจะเป็นไปได้อย่างสมบูรณ์ที่จะใช้ regex แบบเรียกซ้ำตามที่ MizardX โพสต์ไว้ แต่สำหรับสิ่งนี้มันมีประโยชน์มากในการแยกวิเคราะห์ Regexes เดิมทีตั้งใจจะใช้กับภาษาปกติ, การเรียกซ้ำหรือการมีกลุ่มที่สมดุลเป็นเพียงการปะแก้

ภาษาที่กำหนด regexes ที่ถูกต้องเป็นจริงไวยากรณ์บริบทและคุณควรใช้ parser ที่เหมาะสมสำหรับการจัดการ นี่คือตัวอย่างสำหรับโครงการมหาวิทยาลัยสำหรับการแยกวิเคราะห์ regexes แบบง่าย ๆ มันใช้ JavaCC และใช่ความคิดเห็นเป็นภาษาสเปน แต่ชื่อวิธีการอธิบายด้วยตนเองค่อนข้างสวย

SKIP :
{
    " "
|   "\r"
|   "\t"
|   "\n"
}
TOKEN : 
{
    < DIGITO: ["0" - "9"] >
|   < MAYUSCULA: ["A" - "Z"] >
|   < MINUSCULA: ["a" - "z"] >
|   < LAMBDA: "LAMBDA" >
|   < VACIO: "VACIO" >
}

IRegularExpression Expression() :
{
    IRegularExpression r; 
}
{
    r=Alternation() { return r; }
}

// Matchea disyunciones: ER | ER
IRegularExpression Alternation() :
{
    IRegularExpression r1 = null, r2 = null; 
}
{
    r1=Concatenation() ( "|" r2=Alternation() )?
    { 
        if (r2 == null) {
            return r1;
        } else {
            return createAlternation(r1,r2);
        } 
    }
}

// Matchea concatenaciones: ER.ER
IRegularExpression Concatenation() :
{
    IRegularExpression r1 = null, r2 = null; 
}
{
    r1=Repetition() ( "." r2=Repetition() { r1 = createConcatenation(r1,r2); } )*
    { return r1; }
}

// Matchea repeticiones: ER*
IRegularExpression Repetition() :
{
    IRegularExpression r; 
}
{
    r=Atom() ( "*" { r = createRepetition(r); } )*
    { return r; }
}

// Matchea regex atomicas: (ER), Terminal, Vacio, Lambda
IRegularExpression Atom() :
{
    String t;
    IRegularExpression r;
}
{
    ( "(" r=Expression() ")" {return r;}) 
    | t=Terminal() { return createTerminal(t); }
    | <LAMBDA> { return createLambda(); }
    | <VACIO> { return createEmpty(); }
}

// Matchea un terminal (digito o minuscula) y devuelve su valor
String Terminal() :
{
    Token t;
}
{
    ( t=<DIGITO> | t=<MINUSCULA> ) { return t.image; }
}

11

คุณสามารถส่ง regex preg_matchที่จะคืนค่าเท็จถ้า regex ไม่ถูกต้อง อย่าลืมใช้ข้อความ@เพื่อระงับข้อความแสดงข้อผิดพลาด:

@preg_match($regexToTest, '');
  • จะกลับ 1 ถ้า regex //คือ
  • จะส่งคืน 0 ถ้า regex ไม่เป็นไร
  • จะกลับเท็จมิฉะนั้น

6

ตัวอย่างต่อไปนี้โดย Paul McGuire มีพื้นเพมาจาก pyparsing wiki แต่ขณะนี้มีให้บริการผ่านเครื่อง Waybackเท่านั้นให้ไวยากรณ์สำหรับการแยกวิเคราะห์regexes บางส่วนเพื่อวัตถุประสงค์ในการส่งคืนชุดสตริงที่ตรงกัน ด้วยเหตุนี้มันจึงปฏิเสธผู้ที่มีเงื่อนไขการทำซ้ำที่ไม่ได้ จำกัด เช่น '+' และ '*' แต่ควรให้แนวคิดเกี่ยวกับวิธีจัดโครงสร้างตัวแยกวิเคราะห์ที่จะประมวลผล

# 
# invRegex.py
#
# Copyright 2008, Paul McGuire
#
# pyparsing script to expand a regular expression into all possible matching strings
# Supports:
# - {n} and {m,n} repetition, but not unbounded + or * repetition
# - ? optional elements
# - [] character ranges
# - () grouping
# - | alternation
#
__all__ = ["count","invert"]

from pyparsing import (Literal, oneOf, printables, ParserElement, Combine, 
    SkipTo, operatorPrecedence, ParseFatalException, Word, nums, opAssoc,
    Suppress, ParseResults, srange)

class CharacterRangeEmitter(object):
    def __init__(self,chars):
        # remove duplicate chars in character range, but preserve original order
        seen = set()
        self.charset = "".join( seen.add(c) or c for c in chars if c not in seen )
    def __str__(self):
        return '['+self.charset+']'
    def __repr__(self):
        return '['+self.charset+']'
    def makeGenerator(self):
        def genChars():
            for s in self.charset:
                yield s
        return genChars

class OptionalEmitter(object):
    def __init__(self,expr):
        self.expr = expr
    def makeGenerator(self):
        def optionalGen():
            yield ""
            for s in self.expr.makeGenerator()():
                yield s
        return optionalGen

class DotEmitter(object):
    def makeGenerator(self):
        def dotGen():
            for c in printables:
                yield c
        return dotGen

class GroupEmitter(object):
    def __init__(self,exprs):
        self.exprs = ParseResults(exprs)
    def makeGenerator(self):
        def groupGen():
            def recurseList(elist):
                if len(elist)==1:
                    for s in elist[0].makeGenerator()():
                        yield s
                else:
                    for s in elist[0].makeGenerator()():
                        for s2 in recurseList(elist[1:]):
                            yield s + s2
            if self.exprs:
                for s in recurseList(self.exprs):
                    yield s
        return groupGen

class AlternativeEmitter(object):
    def __init__(self,exprs):
        self.exprs = exprs
    def makeGenerator(self):
        def altGen():
            for e in self.exprs:
                for s in e.makeGenerator()():
                    yield s
        return altGen

class LiteralEmitter(object):
    def __init__(self,lit):
        self.lit = lit
    def __str__(self):
        return "Lit:"+self.lit
    def __repr__(self):
        return "Lit:"+self.lit
    def makeGenerator(self):
        def litGen():
            yield self.lit
        return litGen

def handleRange(toks):
    return CharacterRangeEmitter(srange(toks[0]))

def handleRepetition(toks):
    toks=toks[0]
    if toks[1] in "*+":
        raise ParseFatalException("",0,"unbounded repetition operators not supported")
    if toks[1] == "?":
        return OptionalEmitter(toks[0])
    if "count" in toks:
        return GroupEmitter([toks[0]] * int(toks.count))
    if "minCount" in toks:
        mincount = int(toks.minCount)
        maxcount = int(toks.maxCount)
        optcount = maxcount - mincount
        if optcount:
            opt = OptionalEmitter(toks[0])
            for i in range(1,optcount):
                opt = OptionalEmitter(GroupEmitter([toks[0],opt]))
            return GroupEmitter([toks[0]] * mincount + [opt])
        else:
            return [toks[0]] * mincount

def handleLiteral(toks):
    lit = ""
    for t in toks:
        if t[0] == "\\":
            if t[1] == "t":
                lit += '\t'
            else:
                lit += t[1]
        else:
            lit += t
    return LiteralEmitter(lit)    

def handleMacro(toks):
    macroChar = toks[0][1]
    if macroChar == "d":
        return CharacterRangeEmitter("0123456789")
    elif macroChar == "w":
        return CharacterRangeEmitter(srange("[A-Za-z0-9_]"))
    elif macroChar == "s":
        return LiteralEmitter(" ")
    else:
        raise ParseFatalException("",0,"unsupported macro character (" + macroChar + ")")

def handleSequence(toks):
    return GroupEmitter(toks[0])

def handleDot():
    return CharacterRangeEmitter(printables)

def handleAlternative(toks):
    return AlternativeEmitter(toks[0])


_parser = None
def parser():
    global _parser
    if _parser is None:
        ParserElement.setDefaultWhitespaceChars("")
        lbrack,rbrack,lbrace,rbrace,lparen,rparen = map(Literal,"[]{}()")

        reMacro = Combine("\\" + oneOf(list("dws")))
        escapedChar = ~reMacro + Combine("\\" + oneOf(list(printables)))
        reLiteralChar = "".join(c for c in printables if c not in r"\[]{}().*?+|") + " \t"

        reRange = Combine(lbrack + SkipTo(rbrack,ignore=escapedChar) + rbrack)
        reLiteral = ( escapedChar | oneOf(list(reLiteralChar)) )
        reDot = Literal(".")
        repetition = (
            ( lbrace + Word(nums).setResultsName("count") + rbrace ) |
            ( lbrace + Word(nums).setResultsName("minCount")+","+ Word(nums).setResultsName("maxCount") + rbrace ) |
            oneOf(list("*+?")) 
            )

        reRange.setParseAction(handleRange)
        reLiteral.setParseAction(handleLiteral)
        reMacro.setParseAction(handleMacro)
        reDot.setParseAction(handleDot)

        reTerm = ( reLiteral | reRange | reMacro | reDot )
        reExpr = operatorPrecedence( reTerm,
            [
            (repetition, 1, opAssoc.LEFT, handleRepetition),
            (None, 2, opAssoc.LEFT, handleSequence),
            (Suppress('|'), 2, opAssoc.LEFT, handleAlternative),
            ]
            )
        _parser = reExpr

    return _parser

def count(gen):
    """Simple function to count the number of elements returned by a generator."""
    i = 0
    for s in gen:
        i += 1
    return i

def invert(regex):
    """Call this routine as a generator to return all the strings that
       match the input regular expression.
           for s in invert("[A-Z]{3}\d{3}"):
               print s
    """
    invReGenerator = GroupEmitter(parser().parseString(regex)).makeGenerator()
    return invReGenerator()

def main():
    tests = r"""
    [A-EA]
    [A-D]*
    [A-D]{3}
    X[A-C]{3}Y
    X[A-C]{3}\(
    X\d
    foobar\d\d
    foobar{2}
    foobar{2,9}
    fooba[rz]{2}
    (foobar){2}
    ([01]\d)|(2[0-5])
    ([01]\d\d)|(2[0-4]\d)|(25[0-5])
    [A-C]{1,2}
    [A-C]{0,3}
    [A-C]\s[A-C]\s[A-C]
    [A-C]\s?[A-C][A-C]
    [A-C]\s([A-C][A-C])
    [A-C]\s([A-C][A-C])?
    [A-C]{2}\d{2}
    @|TH[12]
    @(@|TH[12])?
    @(@|TH[12]|AL[12]|SP[123]|TB(1[0-9]?|20?|[3-9]))?
    @(@|TH[12]|AL[12]|SP[123]|TB(1[0-9]?|20?|[3-9])|OH(1[0-9]?|2[0-9]?|30?|[4-9]))?
    (([ECMP]|HA|AK)[SD]|HS)T
    [A-CV]{2}
    A[cglmrstu]|B[aehikr]?|C[adeflmorsu]?|D[bsy]|E[rsu]|F[emr]?|G[ade]|H[efgos]?|I[nr]?|Kr?|L[airu]|M[dgnot]|N[abdeiop]?|Os?|P[abdmortu]?|R[abefghnu]|S[bcegimnr]?|T[abcehilm]|Uu[bhopqst]|U|V|W|Xe|Yb?|Z[nr]
    (a|b)|(x|y)
    (a|b) (x|y)
    """.split('\n')

    for t in tests:
        t = t.strip()
        if not t: continue
        print '-'*50
        print t
        try:
            print count(invert(t))
            for s in invert(t):
                print s
        except ParseFatalException,pfe:
            print pfe.msg
            print
            continue
        print

if __name__ == "__main__":
    main()
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.