Кіріспе
Іздеу үлгісін құрайтын таңбалар тізбегі. Регулярлы өрнек (regex немесе regexp деп қысқартылады), кейде рационалды өрнек деп те аталады, – мәтінде сәйкес келетін үлгіні анықтайтын таңбалар тізбегі. Мұндай үлгілер көбінесе жол іздеу алгоритмдерімен "іздеу" немесе "іздеу және алмастыру" операциялары үшін, немесе деректерді тексеру үшін қолданылады. Регулярлы өрнектер әдістемелері теориялық информатика және формальді тілдер теориясында жасалған. Регулярлы өрнектер тұжырымы 1950 жылдары американдық математик Стивен Коул Клин регулярлы тіл тұжырымын ресми түрде қалыптаған кезде пайда болды. Олар Unix мәтіндік өңдеу құралдарымен кеңінен қолданысқа енді. Регулярлы өрнектерді жазу үшін әртүрлі синтаксистер 1980 жылдан бері бар, олардың бірі – POSIX стандарты, ал екіншісі, кеңінен қолданылатыны – Perl синтаксисі. Регулярлы өрнектер іздеу жүйелерінде, сөз өңдегіштер мен мәтін редакторларында іздеу және алмастыру диалогтарында, sed және AWK сияқты мәтіндік өңдеу құралдарында және лексикалық талдауда қолданылады. Көптеген бағдарламалау тілдері регулярлы өрнектерді қолдайды. Кітапханалық іске асырулар көбінесе "қозғалтқыш" деп аталады, және олардың көптеген түрлерін қайта пайдалануға болады.
A regular expression (shortened as regex or regexp), sometimes referred to as rational expression, is a sequence of characters that specifies a match pattern in text. Usually such patterns are used by string searching algorithms for "find" or "find and replace" operations on strings, or for input validation. Regular expression techniques are developed in theoretical computer science and formal language theory. The concept of regular expressions began in the 1950s, when the American mathematician Stephen Cole Kleene formalized the concept of a regular language. They came into common use with Unix text processing utilities. Different syntaxes for writing regular expressions have existed since the 1980s, one being the POSIX standard and another, widely used, being the Perl syntax. Regular expressions are used in search engines, in search and replace dialogs of word processors and text editors, in text processing utilities such as sed and AWK, and in lexical analysis. Regular expressions are supported in many programming languages. Library implementations are often called an "engine", and many of these are available for reuse.
Тарих
Регламентті өрнектер 1951 жылы пайда болды, математик Стивен Коул Клин өзінің математикалық белгісін пайдалану арқылы тұрақты тілдерді сипаттады. Бұл теориялық компьютерлік ғылымда, автоматтар теориясының (есептеу модельдері) және формалды тілдерді сипаттау мен жіктеудің кіші салаларында пайда болды. Үлгілерді сәйкестендірудің басқа ертедегі іске асырылуларына SNOBOL тілі кіреді, ол тұрақты өрнектерді қолданбай, өзінің үлгілерді сәйкестендіру құрылымдарын пайдаланды. Регламентті өрнектер 1968 жылдан бастап екі мақсатта кеңінен қолданыла бастады: мәтіндік редактордағы үлгілерді сәйкестендіру және компилятордағы лексикалық талдау. Бағдарлама түріндегі тұрақты өрнектердің алғашқы пайда болуы Кен Томпсон мәтіндік файлдардағы үлгілерді сәйкестендіру құралы ретінде редакторға Клейннің белгісін енгізген кезде болды. Жылдамдық үшін Томпсон IBM 7094 кодына жай уақытында компиляция (JIT) арқылы тұрақты өрнекті сәйкестендіруді үйлесімді уақыт бөлісу жүйесінде іске асырды, бұл JIT компиляциясының маңызды ерте мысалы. Кейін ол бұл мүмкіндікті Unix редакторына қосты ed, бұл ақырында танымал grep іздеу құралының тұрақты өрнектерді қолдануына әкелді ("grep" - бұл ed редакторындағы тұрақты өрнекті іздеу командасынан алынған сөз: g/re/p, "Жаһандық іздеу, Регулярлы өрнек және Сәйкес келетін жолдарды басып шығару" дегенді білдіреді). Томпсон QED-ді әзірлеген кезде, Дуглас Т. Росс және басқа зерттеушілер тобы компиляторды жобалауда лексикалық талдау үшін қолданылатын тұрақты өрнектерге негізделген құралды іске асырды. Осы тұрақты өрнектердің бастапқы түрлерінің көптеген түрлері 1970 жылдары Bell Labs-те, соның ішінде vi, lex, sed, AWK және expr, және басқа да бағдарламаларда, мысалы Emacs (өзінің жеке, үйлеспейтін синтаксисі мен мінез-құлқы бар) Unix бағдарламаларында қолданылды. Кейіннен регекстер көптеген бағдарламаларда қолданылды, ал бұл алғашқы нысандар 1992 жылы POSIX.2 стандартында стандартталды. 1980 жылдары Perl-де күрделірек regex пайда болды, ол бастапқыда Генри Спенсер (1986) жазған regex кітапханасынан алынған, кейіннен ол Tcl-ге арналған жетілдірілген тұрақты өрнектер деп аталатын іске асыруды жазды. Tcl кітапханасы - жақсартылған орындау сипаттамалары бар гибридті NFA/DFA іске асыру. Спенсердің Tcl тұрақты өрнегін енгізуді қабылдаған бағдарламалық жобаларға PostgreSQL кіреді. Кейін Perl Спенсердің бастапқы кітапханасын кеңейтіп, көптеген жаңа мүмкіндіктерді қосты. Raku (бұрын Perl 6 деп аталатын) жобалаудағы күш-жігердің бір бөлігі - Perl-дің регекс интеграциясын жақсарту және олардың ауқымы мен мүмкіндіктерін кеңейту, сөйлем грамматикасын талдауға рұқсат ету. Нәтижесінде Raku ережелері деп аталатын шағын тіл пайда болды, ол Raku грамматикасын анықтауға және бағдарламашыларға тілдің құралын беруге қолданылады. Бұл ережелер Perl 5.x regex-терінің қолданыстағы мүмкіндіктерін сақтайды, сонымен қатар суб-ережелер арқылы рекурсивті шығу талдаушының BNF стильді анықтауына мүмкіндік береді. Регекстерді құжат және деректер қорын модельдеуге арналған құрылымдалған ақпарат стандарттарында пайдалану 1960 жылдары басталды және ISO SGML (ANSI "GCA 101 1983") сияқты салалық стандарттар біріктірілгенде 1980 жылдары кеңейтілді. Құрылымдық спецификация тілдерінің өзегі регекстерден тұрады. Оның қолданылуы DTD элемент тобының синтаксисінде айқын көрінеді. Регламенттік өрнектерді пайдаланбастан бұрын көптеген іздеу тілдерінде қарапайым жолдамалар, мысалы "*" кез келген таңбалар тізбегіне сәйкес келеді, ал "?" бір таңбаға сәйкес келеді. Бұл туралы деректерді бүгінде файл атаулары үшін glob синтаксисінен және SQL LIKE операторынан табуға болады. 1997 жылдан бастап Филип Хейзел PCRE (Perl Compatible Regular Expressions) бағдарламасын әзірледі, ол Perl regex функционалдығын еліктеуге тырысады және көптеген заманауи құралдар, соның ішінде PHP және Apache HTTP Server қолданады. Бүгінде регекстер бағдарламалау тілдерінде, мәтін өңдеу бағдарламаларында (әсіресе лексикаларда), озық мәтін редакторларында және басқа да бағдарламаларда кеңінен қолданады. Regex қолдау көптеген бағдарламалау тілдерінің стандартты кітапханасының бөлігі болып табылады, соның ішінде Java және Python, және басқалардың синтаксисіне, соның ішінде Perl және ECMAScript. 2010 жылдардың аяғында бірнеше компаниялар PCRE-ге үйлесімді regex қозғалтқыштарының аппараттық, FPGA, GPU-ның CPU-мен салыстырғанда жылдамдығын ұсына бастады.
Үлгілер
Регулярлы өрнектер немесе регекстер термині көбінесе мәтін үлгілерін табуға арналған ерекше, стандартты мәтіндік синтаксисті білдіру үшін қолданылады, бұл төменде сипатталған математикалық жазбадан өзгеше. Регулярлы өрнектегі әрбір символ (яғни, оның үлгісін сипаттайтын жолдағы әрбір символ) метасимвол болып, арнайы мағынаға ие, немесе тура мағынасы бар символ болып табылады. Мысалы, `b.` регексінде, `b` символы тек `b` символына сәйкес келеді, ал `.` метасимволы жаңа жол символын қоспағанда, кез келген символға сәйкес келеді. Сондықтан, бұл регекс мысалы, `b%`, `bx` немесе `b5` тіркестеріне сәйкес келеді. Метасимволдар мен тура символдарды бірге қолдану арқылы белгілі бір үлгідегі мәтінді табуға немесе оның бірнеше даналарын өңдеуге болады. Үлгіге сәйкес келу дәл теңдіктен бастап өте жалпы ұқсастыққа дейін, метасимволдармен басқарылатын деңгейде өзгеруі мүмкін. Мысалы, `.` өте жалпы үлгі, ал `[a-z]` (әріптердің кіші әріптерінен `a` бастап `z` дейін) одан да аз жалпы, ал `b` нақты үлгі (тек `b` символына сәйкес келеді). Метасимволдық синтаксис нақты мақсаттарды ықшам және икемді түрде көрсету үшін жасалған, бұл мәтінді өңдеуді автоматтандыруға және әртүрлі деректерді стандартты ASCII пернетақтасымен оңай теріп енгізуге мүмкіндік береді. Бұл синтаксистегі регулярлы өрнектің қарапайым мысалы – мәтін редакторында екі түрлі жазылған сөзді табу, мысалы, `seriali[sz]e` регексі `serialise` және `serialize` екеуіне де сәйкес келеді. Жоққа шығару символдары да осы мақсатқа қол жеткізе алады, бірақ олардың үлгілері шектеулі, себебі оларда аз метасимволдар бар және қарапайым тілдік базасы бар. Жоққа шығару символдарының әдеттегі қолданылуы – файлдар тізіміндегі ұқсас атауларды табу, ал регекстер көбінесе мәтіндік тізбелерге үлгі сәйкес келтіретін қосымшаларда қолданылады. Мысалы, `^[ \t]+|[ \t]+$` регексі жолдың басында немесе соңында артық бос орындарға сәйкес келеді. Кез келген санды табуға арналған кеңейтілген регулярлы өрнек: `[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?`. Регекс процессор жоғарыда көрсетілген синтаксистегі регулярлы өрнекті ізделіп отырған мәтінді білдіретін жолмен сәйкес келуге және орындалуға болатын ішкі өрнекке аударады. Мүмкін болатын тәсілдердің бірі – Томпсонның құрылыс алгоритмін қолдану, ол нондетерминистік автоматты (NFA) құрастырады, содан кейін оны детерминистік автоматқа айналдырады және нәтижесіндегі детерминистік автоматты (DFA) регулярлы өрнекке сәйкес келетін ішкі тізбелерді табу үшін мақсатты мәтін жолында орындайды. Суретте `s*` регулярлы өрнегінен алынған NFA схемасы `N(s*)` көрсетілген, мұнда `s` өз кезегінде қарапайым регулярлы өрнекті білдіреді, ол қазірдің өзінде NFA `N(s)`-ке рекурсивті түрде аударылған.
Үлгі өрнектердің теңдестігін анықтау
Жоғарыдағы мысалдардың көпшілігінде көрсетілгендей, бірдей нәтижеге жету үшін тұрақты өрнекті құрудың бірнеше тәсілі бар. Екі берілген тұрақты өрнек үшін сипатталған тілдердің тең екенін анықтайтын алгоритм жасау мүмкін; бұл алгоритм әр өрнекті минималды детерминистік шекті күй машинасына дейін азайтады және олардың изоморфты (эквивалентті) екенін анықтайды. Тұрақты өрнектерге арналған алгебралық заңдарды Гишердің әдісімен алуға болады, бұл мысал арқылы жақсы түсіндіріледі: (X+Y)* және (X*Y*)* бірдей тұрақты тілді білдіретінін тексеру үшін, барлық тұрақты өрнектер X, Y үшін, (a+b)* және (a*b*)* тұрақты өрнектері Σ={a,b} алфавиті бойынша бірдей тілді білдіретінін тексеру қажет және жеткілікті. Жалпы алғанда, тұрақты өрнектердегі E=F теңдеуі, оның әртүрлі белгілік тұрақтылармен ауыстырылған әртүрлі айнымалылармен қолданылуы орындалса ғана орындалады. Кез келген тұрақты өрнекті тек Клине жұлдызы мен шекті сөздердің жиынтығы арқылы жазуға болады. Бұл күрделі мәселе. Тұрақты өрнектер қаншалықты қарапайым болса да, оларды жүйелі түрде стандартты түрге қайта жазудың әдісі жоқ. Бұрынғы аксиоманың болмауы жұлдыз биіктігі мәселесіне әкелді. 1991 жылы Декстер Козен тұрақты өрнектерді Клине алгебрасы ретінде аксиоматизациялады, теңдеулік және Хорн аксиомаларын қолданды. Ал 1964 жылы Редко таза теңдеулік аксиомалардың шекті жиынтығы тұрақты тілдердің алгебрасын сипаттай алмайтынын дәлелдеді.
Синтаксисі
Регулярлы өрнек үлгісі мақсатты тізбекке сәйкес келеді. Үлгі атомдар тізбегінен тұрады. Атом – бұл регулярлы өрнек үлгісіндегі бір нүкте, ол мақсатты тізбекке сәйкес келуге тырысады. Ең қарапайым атом – сөздік мағынадағы жазу, бірақ үлгінің атомға сәйкес келетін бөліктерін топтастыру үшін метасимволдар ретінде ( ) пайдалану қажет. Метасимволдар мыналарды құруға көмектеседі: атомдар; қанша атом бар екенін көрсететін сандық белгілер (және ол ашкөз сандық белгі ме, жоқ па); логикалық ЖӘНЕ (OR) символы, ол баламалар жиынтығын ұсынады, және логикалық ЖОҚ (NOT) символы, ол атомның болуын жоққа шығарады; сондай-ақ, аяқталған атомдар үлгісінің бұрынғы атомдарына сілтеме жасау үшін кері сілтемелер. Сәйкестік, тізбектің барлық атомдарына сәйкес келгенде емес, регулярлы өрнектегі барлық үлгі атомдарына сәйкес келгенде жасалады. Идея – барлық сөздік мағынадағы мүмкіндіктердің үлкен тізімін құрастырудың орнына, кішкентай таңбалар үлгісін көптеген ықтимал тізбектер үшін қолдану. Регулярлы өрнек процессорына байланысты шамамен он төрт метасимвол бар, бұл таңбалар контекстке байланысты немесе "эскейп" символдарының алдында, яғни осы жағдайда кері қисық сызық \ болғанда, әдеттегі мағынасын жоғалтуы мүмкін. Қазіргі заманғы және POSIX кеңейтілген регулярлы өрнектер метасимволдарды олардың тура мағынасынан жиірек қолданады, сондықтан "кері қисық сызыққа тәуелділік" немесе "тіс тіркесігі синдромын" болдырмау үшін, олар метасимволдарды тура мағынаға айналдыруға мүмкіндік береді; алайда, бастапқыда олар төрт жақшалы метасимволдар ( ) және { } негізінен тура мағыналы болады, ал олардың әдеттегі мағынасынан "эскейп" арқылы метасимволдарға айналады. Көптеген стандарттар екеуін де қолдайды. Әдеттегі метасимволдар: {}[] ^$.|*+? және \. Эскейп символдары қолданылғанда метасимволға айналатын әдеттегі таңбалар: dswDSW және N.
Шекаралық белгілер
Бағдарламалау тілінде регексті енгізген кезде, олар әдеттегі жол ретінде бейнеленуі мүмкін, сондықтан әдетте тырнақшалармен жазылады; мысалы, C, Java және Python тілдерінде осылай жиі кездеседі, онда регекс re "re" түрінде енгізіледі. Дегенмен, олар көбінесе слэштермен шектеледі, мысалы, re регексі үшін /re/. Бұл ed редакторында пайда болды, онда / іздеу командасы болып табылады, ал /re/ өрнегі үлгіге сәйкес келетін жолдардың диапазонын белгілеу үшін қолданылады, оны екі жақтағы басқа командалармен біріктіруге болады. Ең танымал мысалы – g/re/p, grep ("global regex print") командасы, ол Linux дистрибутивтері сияқты Unix негізіндегі көптеген операциялық жүйелерде кездеседі. Ұқсас тәсіл sed редакторында да қолданылады, онда іздеу және алмастыру s/re/replacement/ арқылы жүзеге асырылады, ал жолдардың диапазонын көрсету үшін үлгілерді үтірмен біріктіруге болады, мысалы, /re1/,/re2/. Бұл жазу тәсілі Perl тілінде кеңінен танымал, онда ол қалыпты жолдардан ерекшеленетін синтаксистің бөлігін құрайды. Кейбір жағдайларда, мысалы, sed және Perl тілдерінде, мазмұнмен қақтығысты болдырмау және мазмұндағы шектегіш белгіні қашудан қорғау үшін баламалы шектегіштерді қолдануға болады. Мысалы, sed командасында s,/,X, командасы / белгісін X белгісімен алмастырады, мұнда үтір шектегіш ретінде қолданылады.
IEEE POSIX стандарты
IEEE POSIX стандарты үш деңгейлі сәйкестікке ие: BRE (Негізгі тұрақты өрнектер), ERE (Кеңейтілген тұрақты өрнектер) және SRE (Жай тұрақты өрнектер). SRE қолданыстан шығарылды, оның орнына BRE пайдаланылады, себебі екеуі де кері үйлесімділікті қамтамасыз етеді. Төмендегі бөлім, символдар кластарын қамтитын, BRE және ERE екеуіне де қолданылады. BRE және ERE бірге жұмыс істейді. ERE ?, +, және | сиымдарын қосады, сондай-ақ BRE-де қажет болатын метасимволдарды ( ) және { } қашардан босату қажеттілігін жояды. Сонымен қатар, регекстер үшін POSIX стандартының синтаксисі сақталғанда, нақты (бірақ POSIX стандартына сәйкес) қолданбаларға арналған қосымша синтаксис болуы мүмкін және көбінесе болады. POSIX.2 кейбір іске асыру ерекшеліктерін нақтыламаса да, BRE және ERE «стандартты» ұсынады, ол көптеген құралдардың әдепкі синтаксисі ретінде қабылданды, онда BRE немесе ERE режимдерін таңдау мүмкіндігі көбінесе қолдау табады. Мысалы, GNU grep келесі опцияларды ұсынады: "grep -E" ERE үшін, "grep -G" BRE үшін (әдепкі), және "grep -P" Perl регекстері үшін. Perl регекстері бай және қуатты атомдық өрнектер жиынтығына ие болып, де-факто стандартқа айналды. Perl-де «негізгі» немесе «кеңейтілген» деңгейлер жоқ. POSIX ERE-дегідей, ( ) және { } қашарлары қашардан босатылмаса, метасимволдар ретінде қарастырылады; басқа метасимволдар контекстке қарай нақты немесе символдық деп анықталады. Қосымша мүмкіндіктерге жалқау сәйкестендіру, кері сілтемелер, аталған топтар және рекурсивті үлгілер кіреді.
Таңба кластары
Таңба класы – бұл тура мағыналы сәйкестендіруден кейінгі ең негізгі регекс тұжырымдамасы. Ол кішкентай таңбалар тізбегін үлкен таңбалар жиынтығына сәйкес етеді. Мысалы, [A Z] ағылшын алфавитіндегі кез келген бас әріпті білдіре алады, ал \d кез келген цифрды білдіре алады. Таңба кластары POSIX деңгейлерінің екісіне де қолданылады. [a Z] (яғни кіші әріптен үлкен әріпке дейін) сияқты таңбалар ауқымын көрсеткенде, компьютердің жергілікті параметрлері мазмұнды таңба кодтамасының сандық ретімен анықтайды. Олар цифрларды осы ретпен сақтай алады, немесе олардың ретін abc zABC Z, немесе aAbBcC zZ деп белгілеуге болады. POSIX стандарты орнатылған регекс процессормен белгілі болатын таңба класын анықтайды. Бұл анықтамалар келесі кестеде:
Сипаттама | POSIX | Perl/Tcl | Vim | Java | ASCII
------- | -------- | -------- | -------- | -------- | --------
ASCII таңбалары | \p{ASCII} | [\x00 \x7F] | | |
Алфа-цифрлық таңбалар | [:alnum:] | \p{Alnum} | [A Za z0 9] | |
Алфа-цифрлық таңбалар плюс " " | \w | \w | \w | [A Za z0 9 ] |
Сөздік емес таңбалар | \W | \W | \W | [^A Za z0 9 ] |
Әліппелік таңбалар | [:alpha:] | \a | \p{Alpha} | [A Za z] |
Бос орын және табу белгісі | [:blank:] | \s | \p{Blank} | [ \t] |
Сөз шекаралары | \b | \< \> | \b | (?<=\W)(?=\w)|(?<=\w)(?=\W) |
Сөз шекаралары емес | \B | | | (?<=\W)(?=\W)|(?<=\w)(?=\w) |
Бақылау таңбалары | [:cntrl:] | \p{Cntrl} | [\x00 \x1F\x7F] | |
Цифрлар | [:digit:] | \d | \d | \p{Digit} немесе \d | [0 9]
Цифрлар емес | \D | \D | \D | [^0 9] |
Көрінетін таңбалар | [:graph:] | \p{Graph} | [\x21 \x7E] | |
Кіші әріптер | [:lower:] | \l | \p{Lower} | [a z] |
Көрінетін таңбалар және бос орын | [:print:] | \p | \p{Print} | [\x20 \x7E] |
Пунктуация таңбалары | [:punct:] | \p{Punct} | [][! "#$%&' *+,./:;<=>? @\^ `{|}~ ] | |
Бос орын таңбалары | [:space:] | \s | \s | \p{Space} немесе \s | [ \t\r\n\v\f]
Бос орын таңбалары емес | \S | \S | \S | [^ \t\r\n\v\f] |
Үлкен әріптер | [:upper:] | \u | \p{Upper} | [A Z] |
Он алтылық цифрлар | [:xdigit:] | \x | \p{XDigit} | [A Fa f0 9] |
POSIX таңба кластарын жақшалы өрнектердің ішінде ғана қолдануға болады. Мысалы, [[:upper:]ab] үлкен әріптерді және кіші әріптер "a" мен "b" сәйкестендіреді. Кейбір құралдар түсінетін қосымша POSIX емес класс – [:word:], ол әдетте [:alnum:] плюс астын сызу белгісі ретінде анықталады. Бұл көптеген бағдарламалау тілдерінде идентификаторларда пайдаланылуы мүмкін таңбалар екендігін көрсетеді. Vim редакторы сөз және сөз басы кластарын (\w және \h белгілерін пайдалана отырып) ажыратады, өйткені көптеген бағдарламалау тілдерінде идентификаторды бастайтын таңбалар басқа орындарда кездесетін таңбалардан өзгеше болады: сандар әдетте алынып тасталады, сондықтан идентификатор POSIX белгісінде \h\w* немесе [[:alpha:] ][[:alnum:] ]* сияқты көрінеді. POSIX регекс стандарттары таңба кластарын шақыратынды басқа регекс түрлерінде POSIX таңба кластары деп атайды. Басқа регекс түрлерінің көпшілігінде POSIX-тің жақшалы өрнектер деп атағандарын сипаттау үшін таңба класы термині қолданылады.
Perl және PCRE
Экспрессивтілігі мен (салыстырмалы) оқуға қолайлылығынан кейін, көптеген басқа құралдар мен бағдарламалау тілдері Perl-дің синтаксисіне ұқсас синтаксисті қабылдады, мысалы Java, JavaScript, Julia, Python, Ruby, Qt, Microsoft-тың .NET Framework және XML Schema. Boost және PHP сияқты кейбір тілдер мен құралдар бірнеше режекс нұсқаларын қолдайды. Perl-дің режекс түзілімдері толықтай бірдей емес және әдетте 1994 жылы жарық көрген Perl 5.0 нұсқасындағы мүмкіндіктердің бір бөлігін ғана іске асырады. Perl кейде басқа тілдерде алғаш пайда болған мүмкіндіктерді де қамтиды. Мысалы, Perl 5.10 PCRE және Python тілдерінде әзірленген синтаксистік кеңейтулерді іске қосты.
Жалқау сәйкестендіру
Python және кейбір басқа да іске асырылымдарда (мысалы, Java) үш кең таралған квантификатор (*, + және ?) әдепкі бойынша ашкөз болып келеді, себебі олар мүмкіндігінше көп символға сәйкес келеді. ".+" регексі (қос тырнақшаларды қоса алғанда) "Ганимед", - деп жалғастырды ол, - Күн жүйесіндегі ең үлкен ай" деген жолға қолданғанда, бүкіл жолға сәйкес келеді (өйткені жолдың басы мен соңы қос тырнақшамен белгіленген), ал "Ганимед" деген алғашқы бөлігіне ғана сәйкес келмейді. Аталған квантификаторларды сұрақ белгісін қосу арқылы ("+?" сияқты) минималды немесе тартымсыз етуге болады, олар осылайша мүмкіндігінше аз символға сәйкес келеді. Мысалы, ".+?" тек "Ганимед" дегенге ғана сәйкес келеді. Квантификаторларды плюс белгісін қосу арқылы иеленуші етуге болады, бұл кері қайтуды (қайта іздеу механизмінде) тоқтатып, тіпті бұл жалпы сәйкестіктің табысқа жетуіне мүмкіндік берсе де, бұлай істеуге жол бермейді: ". *" регексі "Ганимед", - деп жалғастырды ол, - Күн жүйесіндегі ең үлкен ай" деген жолға қолданғанда, жолдың барлығына сәйкес келеді, ал ". *+" регексі мүлдем сәйкес келмейді, себебі . *+ кірістің барлығын, соңғы символдың өзін де жұтып алады. Осылайша, иеленуші квантификаторлар теріске шығарылған символ кластарымен қолданғанда тиімдірек болады, мысалы, "[^"]*+", ол аталған жолға қолданғанда "Ганимед" дегенге сәйкес келеді. Сол функцияны атқаратын тағы бір кең таралған кеңейтім – атомдық топтастыру, ол жақшадағы топтың кері қайтуын тоқтатып қояды. Типтік синтаксис: Мысалы, while both және сәйкес келсе, тек қана сәйкес келеді, себебі механизмге кері қайтуға рұқсат етілмейді, сондықтан "wi" сәйкес келгеннен кейін топты "w" деп өзгертуге тырыса алмайды. Иеленуші квантификаторларды ашкөз және тартымсыз квантификаторларға қарағанда іске асыру оңай, және олар әдетте орындалу кезінде тиімдірек болады.
"Ganymede," he continued, "is the largest moon in the Solar System." matches the entire line (because the entire line begins and ends with a double quote) instead of matching only the first part, "Ganymede,". The aforementioned quantifiers may, however, be made lazy or minimal or reluctant, matching as few characters as possible, by appending a question mark: ".+?" matches only "Ganymede,". quantifiers may be made possessive by appending a plus sign, which disables backing off (in a backtracking engine), even if doing so would allow the overall match to succeed: While the regex ". *" applied to the string
"Ganymede," he continued, "is the largest moon in the Solar System." matches the entire line, the regex ". *+" does not match at all, because . *+ consumes the entire input, including the final ". Thus, possessive quantifiers are most useful with negated character classes, e. g. "[^"]*+", which matches "Ganymede," when applied to the same string. Another common extension serving the same function is atomic grouping, which disables backtracking for a parenthesized group. The typical syntax is For example, while matches both and , only matches because the engine is forbidden from backtracking and so cannot try setting the group to "w" after matching "wi". Possessive quantifiers are easier to implement than greedy and lazy quantifiers, and are typically more efficient at runtime.
Үлгісіз тілдер үлгілері
Қазіргі кездегі барлық тұрақты өрнектер кітапханаларында кездесетін көптеген мүмкіндіктер, тұрақты тілдердің шегінен асып түсетін экспрессивтік қуатты қамтамасыз етеді. Мысалы, көптеген іске асырулар, кіші өрнектерді жақшалармен топтастыруға және олардың сәйкес келетін мәнін сол өрнекте қайта қолдануға мүмкіндік береді (кері сілтемелер). Бұл, басқа нәрселермен қатар, үлгінің "папа" немесе "WikiWiki" сияқты қайталанатын сөздер тізбегіне сәйкес келуін қамтамасыз етеді, бұл формалды тіл теориясында "квадраттар" деп аталады. Мұндай тізбектерге арналған үлгі: (.+)\1. Квадраттар тілі тұрақты емес, сонымен қатар, соғу леммасына сәйкес, контекстсіз де емес. Дегенмен, көптеген қазіргі заманғы құралдар қолдайтын, шексіз кері сілтемелермен сәйкестіру әлі де контекстке сезімтал. Кез келген сандағы кері сілтемелерді сәйкестендірудің жалпы мәселесі NP-толық, ал белгілі алгоритмдердің орындалу уақыты пайдаланылатын кері сілтеме топтарының санына қарай экспоненциалды түрде өседі. Алайда, мұндай құрылымдарды ұсынатын көптеген құралдар, кітапханалар және қозғалтқыштар өз үлгілері үшін "тұрақты өрнек" терминін қолдануын жалғастыруда. Бұл, формалды тіл теориясы мен үлгі сәйкестігінде "тұрақты өрнек" терминінің әртүрлі мағыналарына әкелді. Осы себепті, кейбір адамдар соңғысын сипаттау үшін "regex", "regexp" немесе жай ғана "үлгі" терминдерін қолданады. Perl бағдарламалау тілінің авторы Ларри Уолл, Raku дизайны туралы эссесінде былай жазады:
Блокquote|1="Regular expressions" [ ] нақты тұрақты өрнектермен тек шешімді түрде байланысты. Дегенмен, бұл термин біздің үлгіні сәйкестендіру қозғалтқыштарымыздың мүмкіндіктерімен бірге өсті, сондықтан мен тілдік қажеттілікпен күресуге тырыспаймын. Алайда, мен оларды әдетте "regex" (немесе "regexen", егер мен англосаксондық көңіл-күйде болсам) деп атаймын, сондай-ақ 1994 жылы пайда болған "lookaround" сияқты бірнеше күрделі кеңейтулерді де атауға болады. Lookaround сәйкестіктің айналасын анықтайды, бірақ сәйкестіктің өзіне кірмейді, бұл тек жол іздеу жағдайында ғана маңызды ерекшелік. Олардың кейбіреулерін, қоршаған ортаны тілдің бөлігі ретінде қарастыру арқылы тұрақты тілде модельдеуге болады. 1=(?= ) және (?! ) ең болмағанда 1994 жылдан бері, Perl 5-тен бастап қолданылып келеді. 1=(?<= ) және (?<! ) кері қаралымдары 1997 жылдан бері, Ілия Захаревичтің Perl 5.005-ке жасаған өзгертулерінде куәландырылған.
Орындау және жұмыс істеу уақыты
Бар ең кемі үш түрлі алгоритм бар, олар берілген регекстің жолмен сәйкес келе ма және қалай сәйкес келетінін анықтайды. Ең көне және ең жылдамы формальды тіл теориясының нәтижесіне негізделген, ол кез келген нондетерминистік автоматты (NFA) детерминистік автоматқа (DFA) түрлендіруге мүмкіндік береді. DFA нақты түрде құрылып, содан кейін алынған кіріс жолында бір символдан кейін символмен орындалуы мүмкін. m өлшемді регекс үшін DFA құрастыру O(2^m) уақыт және жадты қажет етеді, бірақ n өлшемді жолда O(n) уақытында орындалуы мүмкін. Ескеріңіз, сандық көрсеткіштер сияқты қысқартулар кеңейтілгеннен кейін өрнектің өлшемі есептеледі. Балама тәсіл – NFA-ны тікелей модельдеу, әр DFA күйін қажет болғанда құру және келесі қадамда оны жою. Бұл DFA-ны жасырын қалдырады және экспоненциалды құрылыс шығындарынан сақтайды, бірақ орындалу құны O(mn) дейін өседі. Нақты тәсіл DFA алгоритмі деп аталады, ал жасырын тәсіл NFA алгоритмі деп аталады. NFA алгоритміне кэш қосу көбінесе «жалтақ DFA» алгоритмі деп аталады, немесе DFA алгоритмі деп аталады, ешқандай айырма жасамай. Бұл алгоритмдер жылдам, бірақ топталған қосымша өрнектерді, жалтақ сандықтарды және ұқсас мүмкіндіктерді еске алу үшін оларды пайдалану қиын. Қазіргі заманғы іске асырулардың арасында Кокс кодына негізделген re1, re2 және sregex отбасы бар. Үшінші алгоритм – үлгіні кіріс жолымен кері қадаммен сәйкестендіру. Бұл алгоритм әдетте NFA деп аталады, бірақ бұл терминология шатастыру тудыруы мүмкін. Оның орындалу уақыты экспоненциалды болуы мүмкін, қарапайым іске асырулар алмасу және шексіз сандықтарды қамтитын өрнектермен сәйкес келгенде, алгоритм экспоненциалды түрде өсетін кіші жағдайлардың санын қарастыруға мәжбүр болады. Бұл Regular Expression Denial of Service (ReDoS) деп аталатын қауіпсіздік мәселесін тудыруы мүмкін. Кері қадаммен іске асыру нашар жағдайда ғана экспоненциалды кепілдік береді, бірақ олар әлдеқайда икемді және күшті. Мысалы, кері сілтемелерді пайдалануға мүмкіндік беретін немесе Perl енгізген әртүрлі кеңейтулерді іске асыратын кез келген іске асыру кері қадамды қамтуы керек. Кейбір іске асырулар екі алгоритмнің де артықшылықтарын алуға тырысады, алдымен жылдам DFA алгоритмін іске қосып, сәйкестік кезінде кері сілтеме кездескенде ғана ықтимал баяу кері қадам алгоритміне қайта оралады. GNU grep (және оның негізіндегі gnulib DFA) осындай стратегияны қолданады. Бойер-Мур (BM) алгоритмдері және кері сканерлеу сияқты DFA-ны оңтайландырудың байланысты әдістерін қолдану арқылы сызықтық емес орындалу уақытын алуға қол жеткізілді. POSIX синтаксисі мен кеңейтулерінің кең спектрін қолдайтын GNU grep, алғашқы өту үшін BM-ді пайдаланады, содан кейін жасырын DFA-ны пайдаланады. Wu agrep, шамамен сәйкестікті іске асырады, BDM-де (артқа DAWG сәйкестігінде) DFA-ға алдын ала сүзгілеуді біріктіреді. NR grep-тің BNDM-і BDM техникасын Shift немесе бит деңгейімен параллелизммен кеңейтеді. Кері сілтемелер үшін кері қадамға бірнеше теориялық баламалар бар, олардың «экспоненттері» тек кері сілтемелер санына байланысты, POSIX сияқты кейбір регекс тілдерінің тұрақты қасиеті. Кері қадамды емес NFA-ны әр кері сілтеме жазбасы үшін көшіретін бір қарапайым әдістің күрделілігі n ұзындығындағы және k кері сілтемелері бар RegExp үшін {\mathrm O}(n^{2k+2}) уақыт және {\mathrm O}(n^{2k+1}) жадты құрайды. Жақында жад автоматтарына негізделген теориялық жұмыс қолданылған «белсенді» айнымалы түйіндерге негізделген тығыз шекті және кейбір кері сілтемеленген регексптер үшін полиномиялық мүмкіндікті ұсынады.
Юникод
Теориялық тұрғыдан алғанда, кез келген токен жиынтығы, алдын ала анықталған жағдайда, тұрақты өрнектермен сәйкес келуі мүмкін. Тарихи іске асырулар тұрғысынан, regex-тер бастапқыда ASCII таңбаларын өздерінің токендер жиынтығы ретінде пайдалану үшін жазылған, бірақ regex кітапханалары көптеген басқа таңбалар жиынтығын қолдады. Көптеген қазіргі заманғы regex қозғалтқыштары Юникодты кем дегенде біраз деңгейде қолдайды. Көптеген жағдайларда таңбалар жиынтығының қандай екені маңызды емес, бірақ Unicode-ті қолдау үшін regex-терді кеңейту кезінде кейбір мәселелер туындайды. Қолданылатын кодтау. Кейбір regex кітапханалары абстрактілік Юникод таңбалары орнына белгілі бір кодтаумен жұмыс істеуді күтеді. Олардың көбіне UTF-8 кодтамасы қажет, ал басқалары UTF-16 немесе UTF-32 күтуі мүмкін. Керісінше, Perl және Java кодтамаларға бейтарап, оның орнына ішкі кодталған таңбалар бойынша жұмыс істейді. Қолдау көрсетілетін Unicode диапазоны. Көптеген regex қозғалтқыштары тек Негізгі көптілді жазықтықты, яғни тек 16 битпен кодталатын таңбаларды қолдайды. Қазіргі уақытта (2016 жылғы жағдай бойынша) тек бірнеше regex қозғалтқыштары (мысалы, Perl және Java) толық 21 биттік Unicode диапазонын басқара алады. ASCII-ге бағдарланған конструкцияларды Unicode-қа кеңейту. Мысалы, ASCII негізделген іске асыруларда [x y] түріндегі таңбалар диапазоны барлық жерде жарамды, егер x және y кодтық нүктелері [0x00,0x7F] аралығында болса және codepoint(x) ≤ codepoint(y) болса. Мұндай таңбалар диапазондарының Unicode-қа табиғи кеңейтілуі тек қана аяқ нүктелерінің [0x00,0x7F] талабын [0x0000,0x10FFFF] деген талапқа өзгертеді. Алайда іс жүзінде бұл жиі орын алмайды. Кейбір іске асырулар, мысалы gawk, Unicode блоктарын таңбалар диапазондарына кесіп өтуге мүмкіндік бермейді. [0x61,0x7F] сияқты диапазон жарамды, өйткені екі ұшы да Basic Latin блогына кіреді, [0x0530,0x0560] сияқты, өйткені екі ұшы да Армения блогына кіреді, бірақ [0x0061,0x0532] сияқты диапазон жарамсыз, өйткені ол бірнеше Unicode блоктарын қамтиды. Басқа қозғалтқыштар, мысалы Vim редакторы, блоктарды кесіп өтуге мүмкіндік береді, бірақ таңбалар мәндері 256-дан артық болмауы керек. Үлкен-кіші әріптерге сезімталдық. Кейбір үлкен-кіші әріптерге сезімталдық белгілері тек ASCII таңбаларына әсер етеді. Басқа белгілер барлық таңбаларға әсер етеді. Кейбір қозғалтқыштарда екі түрлі белгі бар, біреуі ASCII үшін, екіншісі Unicode үшін. POSIX кластарына жататын таңбалардың нақты саны да әр түрлі. Үлкен-кіші әріптерге сезімталдықтың туыстары. ASCII-де үлкен-кіші әріптердің ерекшелігі болғандықтан, үлкен-кіші әріптерге сезімталдық мәтіндік іздеуде логикалық ерекшелікке айналды. Юникодта деванагари сияқты үлкен-кіші әріптерсіз әріптер енгізілген. Бұл үшін үлкен-кіші әріптерге сезімталдық қолданылмайды. Қытайша сияқты жазулар үшін тағы бір айырмашылық логикалық көрінеді: дәстүрлі және оңайлатылған. Араб жазуында бастапқы, орта, соңғы және оқшауланған позицияға сезімталдық қажет болуы мүмкін. Жапон тілінде хирагана мен катакананың арасындағы сезімсіздік кейде пайдалы. Нормализация. Юникодта біріктірілетін таңбалар бар. Ескі жазу машиналары сияқты, жай негізгі таңбалар (ақ бос орындар, тыныш белгілер, символдар, цифрлар немесе әріптер) бір немесе бірнеше аралықсыз символдармен (әдетте диакритика, акцент белгілері сияқты әріптерді өзгерту) біріктірілуі мүмкін, бір жалғыз басылатын таңбаны құру үшін; бірақ Юникодта да шектеулі алдын ала құрастырылған таңбалар бар, яғни бір немесе бірнеше біріктірілетін таңбаларды қамтитын таңбалар. Негізгі таңба + біріктірілетін таңбалар тізбегі бірдей бір алдын ала құрастырылған таңбамен сәйкес болуы керек (осы біріктіру тізбектерінің тек кейбіреуін ғана бір Юникод таңбасына алдын ала құрастыруға болады, бірақ Юникодта көптеген басқа біріктіру тізбектері мүмкін және әртүрлі тілдер үшін қажет, бастапқы негізгі таңбадан кейін бір немесе бірнеше біріктіру таңбаларын қолдану; бұл біріктіру тізбектерінде негізгі таңба немесе ішінара алдын ала құрастырылған таңбаларды біріктіру болуы мүмкін, бірақ міндетті түрде канондық тәртіппен емес және міндетті түрде канондық алдын ала құрастыруларды қолданбайды). Негізгі таңбалардың тізбектерін стандарттау процесі + біріктіру таңбаларын осы каноникалық теңдеу тізбектерін қайта реттеп, оларды каноникалық ретке келтіруден бұрын (және кейбір біріктіру таңбаларын жетекші базалық таңбаға қайта құру) нормализация деп аталады. Жаңа басқару кодтары. Юникод байт реті белгілері мен мәтін бағыт белгілерін енгізді. Бұл кодтармен арнайы жұмыс істеу керек болуы мүмкін. Unicode блоктары, скрипттері және көптеген басқа таңба қасиеттері үшін таңба кластарын енгізу. Блок қасиеттері скрипт қасиеттеріне қарағанда әлдеқайда пайдалы, өйткені блок бірнеше түрлі скрипттерден код нүктелерін, ал скрипт бірнеше түрлі блоктардан код нүктелерін қамти алады. Perl және кітапханада \p{InX} немесе \p{Block=X} түріндегі қасиеттер X блогындағы таңбаларды, ал \P{InX} немесе \P{Block=X} сол блокқа кірмейтін код нүктелерін сәйкестендіреді. Сонымен қатар, \p{Armenian}, \p{IsArmenian} немесе \p{Script=Armenian} Армения скриптіндегі кез келген таңбаны сәйкестендіреді. Жалпы алғанда, \p{X} бинарлық қасиеті X немесе жалпы санат X-ке ие кез келген таңбаны сәйкестендіреді. Мысалы, \p{Lu}, \p{Uppercase Letter} немесе \p{GC=Lu} кез келген үлкен әріпті сәйкестендіреді. Бинарлық қасиеттер, жалпы санаттар емес, \p{White Space}, \p{Alphabetic}, \p{Math} және \p{Dash} кіреді. Бинарлық емес қасиеттердің мысалдары \p{Bidi Class=Right to Left}, \p{Word Break=A Letter} және \p{Numeric Value=10} болып табылады.
Тілдік қолдау
Көптеген жалпы мақсаттағы бағдарламалау тілдері режекс мүмкіндіктерін не туғаннан, не кітапханалар арқылы қолдайды. Толыққанды қолдау келесілерде қамтылған:
Қолданылуы
Регулярлы өрнектер мәтін өңдеудің, және жалпы алғанда, мәтіндік емес деректермен жұмыс істеудің түрлі тапсырмаларында пайдалы. Көптеген қолданыс салаларының ішінде деректерді тексеру, деректерді жинау (әсіресе вебтен дерек жинау), деректерді қайта өңдеу, қарапайым талдау, синтаксистік бөліштерді көрсету жүйелерін құру және тағы да көптеген міндеттер бар. Регулярлы өрнектер интернет іздеу жүйелерінде тиімді болғанымен, оларды бүкіл деректер базасында өңдеу, өрнектің күрделігіне және құрылымына байланысты, компьютерлік ресурстарды тым көп жұмсауы мүмкін. Көп жағдайда жүйе әкімшілері регулярлы өрнектерге негізделген сұраныстарды ішкі жүйеде орындай білсе де, көптеген іздеу жүйелері бұл мүмкіндікті жалпы қолданушыларға ұсынбайды. Google Code Search және Exalead осы ереженің ерекше жағдайлары. Дегенмен, Google Code Search 2012 жылдың қаңтар айында тоқтатылды.
Индукция
Үлгілі өрнектерді мысалдар жиынтығы негізінде жиі жасауға болады ("сауықтыру" немесе "оқыту"). Бұл жүйелі тілдерді индукциялау деп аталады және есептеу оқыту теориясындағы грамматикалық индукцияның жалпы мәселесінің бір бөлігі болып табылады. Формальды түрде, егер жүйелі тілдегі тізбектердің мысалдары берілсе, және мүмкін, сол жүйелі тілге жатпайтын тізбектердің мысалдары да берілсе, онда сол тілдің грамматикасын, яғни осы тілді құратын үлгілі өрнекті тудыруға болады. Барлық жүйелі тілдерді осылай индукциялау мүмкін емес (шектегі тілдерді анықтауға қараңыз), бірақ көптеген тілдерді индукциялауға болады. Мысалы, {1, 10, 100} мысалдар жиынтығы және {11, 1001, 101, 0} теріс жиынтығы (қарсы мысалдар) 1⋅0* (1-ден кейін нөл немесе одан көп 0-лер) үлгілі өрнегін индукциялау үшін қолданылуы мүмкін.