Претрупаните РИ могат да използват много групи, едновременно и за да прихващат интересуващите ги части от символния низ, и за да групират и структурират самия РИ. В сложните РИ става трудно да се проследяват номерата на групите. Съществуват две възможности, които помагат при решаването на този проблем. И двете използват общ синтаксис за разширение на регулярните изрази, така че най-напред ще трябва да разгледаме него.
Perl 5 добави няколко допълнителни възможности към стандартните регулярни изрази, и повечето от тях се поддържат от модула re на Питон. Беше много трудно да се избере нов метасимвол, който да е достъпен чрез натискането на един клавиш, или да се подберат нови специални последователности, започващи с "\", които да представят новите възможности, без при това регулярните изрази на Perl да станат объркващо различни от стандартните РИ. Ако за нов метасимвол беше избран "&", например, тогава старите изрази ще приемат че "&" е обикновен символ и той няма да е избегнат чрез \& или [&]. Избраното решение беше като разширителен синтаксис да се използва (?...). По-рано, "?" непосредствено след скобата би се считало за синтактична грешка, понеже "?" нямаше да има какво да повтаря. По този начин се избягват всякакви проблеми със съвместимостта. Символите непосредствено след "?" показват какво разширение ще се използва, така че (?=foo) е едно (позитивно изпреварващо твърдение), а (?:foo) е друго (неприхващаща група, съдържаща подизраза foo).
Питон добавя още разширителен синтаксис към този на Perl. Ако първият символ след въпросителния знак е "P", трябва да знаете, че това е разширение, специфично за Питон. В момента има само две такива разширения: (?P<име>...) дефинира именувана група и (?P=име) е отпратка към вече именувана група. Ако бъдещите версии на Perl 5 добавят подобни възможности, но чрез различен синтаксис, модулът re ще бъде променен така, че да поддържа този синтаксис, като специфичния синтаксис за Питон ще бъде запазен от съображения за съвместимост.
Вече разгледахме общия синтаксис на разширенията, така че можем да се върнем към възможностите, които опростяват работата с групи в сложни РИ. Тъй като групите са номерирани от ляво на дясно, а един сложен израз може да използва много групи, може да стане много трудно да проследявате правилното номериране, а и е доста неприятно да промяняте такъв сложен РИ. Само да добавите нова група някъде в началото и незабавно променяте номерацията на всичко след нея.
Първо, понякога ще ви се иска да използвате една група само за да обособите част от регулярния израз, без всъщност да се интересувате от извличането на съдържанието, с което тя си пасва. Можете изрично да обявите това чрез използването на една неприхващаща група: (?:...), където в скобите можете да поставите какъвто и да е регулярен израз.
>>> m = re.match("([abc])+", "abc") >>> m.groups() ('c',) >>> m = re.match("(?:[abc])+", "abc") >>> m.groups() ()
С изключение на факта, че не можете да извлечете пасналото и съдържание, неприхващащата група се държи точно както прихващащата - вътре в нея можете да поставите каквото и да било, можете да я повтаряте с повтарящ метасимвол като "*", и да я влагате в други групи (били те прихващащи или неприхващащи). (?:...) е особено полезна когато променяте вече съществуваща група, тъй като можете да добавяте нови групи без да променяте номерацията на останалите. Тук трява да отбележим, че от гледна точка на производителността на търсенето, няма разлика между прихващащи и неприхващащи групи; нито една от двете форми не е по-бърза от другата.
Втората (и по-забележителна) възможност са именуваните групи. Вместо да се обръщате към тях по номер, групите могат да отговарят на име.
Синтаксисът на именуваната група е едно от специфичните за Питон разширения: (?P<име>...). Очевидно, име е името на групата. С изключение на асоциирането на име с група, именуваните групи също се държат точно като прихващащите групи. Всички методи на класа MatchObject, които работят с прихващащи групи, приемат целочислени аргументи, за да се обръщат към номера на групи, или символен низ, съдържащ име на група. На именуваните групи също се присвояват номера, така че за една такава група можете да извлечете информация по два различни начина:
>>> p = re.compile(r'(?P<word>\b\w+\b)') >>> m = p.search( '(((( Lots of punctuation )))' ) >>> m.group('word') 'Lots' >>> m.group(1) 'Lots'
InternalDate = re.compile(r'INTERNALDATE "' r'(?P<day>[ 123][0-9])-(?P<mon>[A-Z][a-z][a-z])-' r'(?P<year>[0-9][0-9][0-9][0-9])' r' (?P<hour>[0-9][0-9]):(?P<min>[0-9][0-9]):(?P<sec>[0-9][0-9])' r' (?P<zonen>[-+])(?P<zoneh>[0-9][0-9])(?P<zonem>[0-9][0-9])' r'"')
m.group('zonem')
, вместо да се
помни, че трябва да се извлече група 9.
Тъй като синтаксисът за отпратките се обръща към номера на групата, в израз като (...)\1 естествено съществува вариант, който използва името на групата вместо номера. Това също е разширение на Питон: (?P=име) показва, че съдържанието на групата, наречена име, трябва отново да бъде открито на текущата позиция. Регулярният израз за намиране на повтарящи се думи, (\b\w+)\s+\1 може да бъде написан и като (?P<word>\b\w+)\s+(?P=word):
>>> p = re.compile(r'(?P<word>\b\w+)\s+(?P=word)') >>> p.search('Paris in the the spring').group() 'the the'