Hur man skriver ett reguljärt uttryck. Mallen för att söka efter valfri HTML-tagg ser förvånansvärt enkel ut


Om du någonsin har fått jobba med kommandorad, använde du förmodligen filnamnsmasker. Till exempel, för att radera alla filer i den aktuella katalogen som börjar med bokstaven "d", kan du skriva rm d* .

Reguljära uttryck är ett liknande men mycket kraftfullare verktyg för att hitta strängar, kontrollera dem mot ett mönster och mer. liknande arbete. Det engelska namnet för detta verktyg är Vanliga uttryck eller bara Regexp. Strängt taget är reguljära uttryck ett speciellt språk för att beskriva strängmönster.

Implementeringen av detta verktyg varierar i olika språk programmering, men inte mycket. I den här artikeln kommer vi främst att fokusera på Perl implementering Kompatibla reguljära uttryck.

Grundläggande syntax

Först och främst är det värt att notera att vilken sträng som helst i sig är ett reguljärt uttryck. Så uttrycket Haha kommer uppenbarligen att motsvara strängen "Haha" och bara det. Reguljära uttryck är skiftlägeskänsliga, så strängen "haha" (gemener) kommer inte längre att matcha uttrycket ovan.

Men här bör du vara försiktig - precis som alla språk har reguljära uttryck specialtecken som måste undvikas. Här är deras lista: . ^ $ * + ? ( ) \ | () . Skärmning utförs på vanligt sätt- lägga till \ före specialtecknet.

Karaktärsuppsättning

Anta att vi i texten vill hitta alla interjektioner som betecknar skratt. Det är bara det att Haha inte kommer att passa oss - trots allt kommer "Hehe", "Hoho" och "Heehee" inte att falla under det. Ja, och problemet med fallet med den första bokstaven måste lösas på något sätt.

Här kommer uppsättningar till undsättning - istället för att ange ett specifikt tecken kan vi skriva ner en hel lista, och om någon av de listade tecknen finns på angiven plats i strängen som undersöks, kommer strängen att anses vara lämplig. Uppsättningar skrivs inom hakparenteser - mönstret kommer att matcha något av tecknen "a", "b", "c" eller "d".

Invändigt set b O De flesta specialtecken behöver inte escapes, men att använda \ framför dem kommer inte att betraktas som ett fel. Det är fortfarande nödvändigt att escape tecknen "\" och "^", och helst "]" (så, står för något av tecknen "]" eller "[", medan [x] bara är sekvensen "[ x]"). Det till synes ovanliga beteendet hos reguljära uttryck med tecknet "]" bestäms faktiskt av välkända regler, men det är mycket lättare att helt enkelt undkomma denna karaktär än att komma ihåg dem. Dessutom måste du undkomma tecknet "-", det används för att ställa in intervall (se nedan).

Om tecknet ^ skrivs omedelbart efter [ , kommer uppsättningen att få motsatt betydelse - alla andra tecken än de angivna kommer att anses lämpliga. Så mönstret [^xyz] matchar vilket tecken som helst, utom faktiskt "x", "y" eller "z".

Så, ansöker detta verktyg i vårt fall, om vi skriver [Xx][aoie]x[aoie] , så kommer var och en av strängarna "Haha", "hehe", "heehe" och till och med "hoho" att matcha mönstret.

Fördefinierade teckenklasser

För vissa uppsättningar som används ganska ofta finns det speciella mallar. Så för att beskriva ett blanksteg (mellanslag, tabb, radbrytning), används \s, för siffror - \d, för latinska tecken, siffror och understreck "_" - \w.

Om det är nödvändigt att beskriva någon karaktär alls, används en prick - för detta. . Om de angivna klasserna skrivs med en stor bokstav (\S , \D , \W), kommer de att ändra sin betydelse till det motsatta - inte mellanslagstecken, alla tecken som inte är en siffra och alla andra tecken än latin, siffror eller understreck.

Med hjälp av reguljära uttryck är det också möjligt att kontrollera en linjes position i förhållande till resten av texten. Uttrycket \b betecknar en ordgräns, \B betecknar en icke-ordsgräns, ^ anger början av text och $ anger slutet. Så, enligt mönstret \bJava\b i strängen "Java och JavaScript" kommer det att finnas de första 4 tecknen, och enligt mönstret \bJava\B - tecken från den 10:e till den 13:e (som en del av ordet " JavaScript").

Avstånd

Du kan behöva ange en uppsättning som innehåller bokstäver, till exempel från "b" till "f". Istället för att skriva [bvgdeziclmnoprostuf] kan du använda intervallmekanismen och skriva [b-f] . Så, mönstret x motsvarar strängen "xA6", men matchar inte "xb9" (för det första på grund av att endast versaler anges i intervallet, och för det andra på grund av att 9 inte ingår i intervallet 0 -åtta).

Avståndsmekanismen är särskilt relevant för det ryska språket, eftersom det inte finns någon konstruktion som liknar \w för den. För att beteckna alla bokstäver i det ryska alfabetet kan du använda mönstret [а-яА-ЯёЁ] . Observera att bokstaven "ё" inte ingår i det allmänna bokstäversortimentet och måste anges separat.

Kvantifierare (anger antalet repetitioner)

Låt oss gå tillbaka till vårt exempel. Tänk om interjektionen "skrattar" hade mer än en vokal mellan x-en, som "Haahaaaa"? Vår gamla ordinarie säsong kommer inte längre att kunna hjälpa oss. Här måste vi använda kvantifierare.

Observera att kvantifieraren endast gäller för tecknet som kommer före det.

Några vanligt använda konstruktioner har fått speciella beteckningar i det reguljära uttrycksspråket:

Således kan vi med hjälp av kvantifierare förbättra vår interjektionsmall till [Xx][aoei]+x[aoei]* , och den kommer att kunna känna igen strängarna "Haaha", "heeeeeeh" och "Heeeeee".

Lat kvantifiering

Anta att vi står inför uppgiften att hitta alla HTML-taggar i en sträng

tproger- min favorit sida om programmering!

Den självklara lösningen<.*>kommer inte att fungera här - det kommer att hitta hela strängen, eftersom den börjar med en stycketagg och slutar med den. Det vill säga innehållet i taggen kommer att betraktas som strängen

P> tproger- min favorit sida om programmering!

Detta beror på att kvantifieraren som standard fungerar enligt den sk. girig algoritm - försöker returnera den längsta möjliga strängen som matchar villkoret. Det finns två sätt att lösa problemet. Det första är att använda uttrycket<[^>]*> , vilket förhindrar att den räta vinkelparentesen betraktas som innehållet i taggen. Den andra är att förklara kvantifieraren inte girig, men lat. Görs detta genom att lägga till ett tecken till höger om kvantifieraren? . De där. för att söka efter alla taggar kommer uttrycket att förvandlas till<.*?> .

Avundsjuk kvantifiering

Ibland, för att öka hastigheten på sökningen (särskilt i de fall där strängen inte matchar det reguljära uttrycket), kan du använda algoritmen för att förhindra att algoritmen återgår till tidigare söksteg för att hitta möjliga matchningar för resten av vanligt uttryck. Det kallas svartsjuk kvantifiering. En kvantifierare görs avundsjuk genom att lägga till ett +-tecken till höger. En annan användning av svartsjuk kvantifiering är att eliminera oönskade matchningar. Så, mönstret ab*+a i strängen "ababa" matchar endast de tre första tecknen, men inte tecknen från det tredje till det femte, eftersom tecknet "a", som är i den tredje positionen, har redan använts för det första resultatet.

Fästgrupper

För vår "skrattar" interjektionsmall finns det väldigt lite kvar - att ta hänsyn till att bokstaven "x" kan förekomma mer än en gång, till exempel "Hahahahaaahahoo", eller det kan till och med sluta med bokstaven "x". Vi behöver förmodligen använda en kvantifierare för gruppen [aioe]+x här, men om vi bara skriver [aioe]x+ , så kommer + kvantifieraren att gälla endast för tecknet "x", och inte för hela uttrycket. För att fixa detta måste uttrycket omges av parentes: ([aioe]x)+ .

Således blir vårt uttryck [Xx]([aioe]x?)+ - först kommer ett versaler eller gemener "x", och sedan ett godtyckligt antal vokaler som inte är noll, som (möjligen, men inte nödvändigtvis) är varvat med enstaka gemener "x". Detta uttryck löser dock problemet bara delvis - sådana rader som till exempel "hihaheh" kommer också att falla under detta uttryck - någon kanske skrattar så, men antagandet är mycket tveksamt. Uppenbarligen kan vi bara använda uppsättningen av alla vokaler en gång, och då måste vi på något sätt lita på resultatet av den första sökningen. Men hur?…

Lagra sökresultatet efter grupp (feedback)

Det visar sig att resultatet av sökningen efter hakparentesgruppen skrivs till en separat minnesplats, åtkomst till vilken är tillgänglig för användning i efterföljande delar av det reguljära uttrycket. För att återgå till uppgiften att hitta HTML-taggar på en sida, kan vi behöva inte bara hitta taggarna, utan också ta reda på deras namn. Det är här ett reguljärt uttryck kan hjälpa oss.<(.*?)> .

tproger- min favorit sida om programmering!

Sökresultat för alla reguljära uttryck: "

», « », «», « », «», «

».
Sökresultat för den första gruppen: "p", "b", "/b", "i", "/i", "/i", "/p".

Resultatet av en gruppsökning kan refereras med uttrycket \n , där n är en siffra från 1 till 9. Till exempel, uttrycket (\w)(\w)\1\2 matchar strängarna "aaaa", " abab", men matchar inte " aabb.

Om uttrycket tas inom parentes endast för att tillämpa en kvantifierare på det (det är inte planerat att komma ihåg sökresultatet för den här gruppen), ska den första parentesen läggas till direkt?: , till exempel (?:+\w) .

Med denna mekanism kan vi skriva om vårt uttryck till formen [Xx]([aoie])x?(?:\1x?)* .

Uppräkning

För att kontrollera om en sträng uppfyller minst ett av mönstren kan du använda analogen till den booleska OR-operatorn, som skrivs med symbolen | . Så, mönstret Anna|Loneliness inkluderar strängarna "Anna" respektive "Loneliness". Det är särskilt bekvämt att använda enums inuti konsolgrupper. Så till exempel (?:a|b|c|d) är helt ekvivalent (in det här fallet det andra alternativet är att föredra på grund av prestanda och läsbarhet).

Med denna operator kan vi lägga till vårt interjektion reguljära uttryck förmågan att känna igen skratt som "Ahahaah" - det enda flin som börjar med en vokal: [Xx]([aoie])x?(?:\1x?)*| [Ah]x?(?:ah?)+

Användbara tjänster

Du kan öva och/eller testa ditt reguljära uttryck på viss text utan att skriva kod med tjänster som RegExr, Regexpal eller Regex101. Den senare ger dessutom en kort förklaring av hur regex fungerar.

Du kan ta reda på hur det reguljära uttrycket som föll i dina händer fungerar med hjälp av tjänsten

Reguljära uttryck är ett allmänt använt sätt att beskriva mönster för att hitta text och kontrollera att text stämmer överens med ett mönster. Särskilda metatecken låter dig specificera till exempel att du letar efter en delsträng i början av inmatningssträngen, eller speciellt nummer delsträngsrepetitioner.

Vid första anblicken ser reguljära uttryck skrämmande ut (ja, det är ännu läskigare vid andra anblicken ;)).

Jag rekommenderar starkt att du "leker" med demoprogrammet för Windows REStudio som medföljer distributionen - detta kommer att tillåta dig att bättre förstå hur reguljära uttryck fungerar och felsöka dina egna uttryck. Dessutom innehåller TestRExp många exempeluttryck.

Nedan finns en beskrivning av en delmängd av reguljära uttryckssyntax som fungerar i nästan alla implementeringar, och som stöds av mitt Delphi-bibliotek som ingår som standard i Lazarus (Free Pascal) .

Låt oss börja vår bekantskap med reguljära uttryck!

Enkel jämförelse

Vilken karaktär som helst matchar sig själv, såvida den inte tillhör de speciella metatecken som beskrivs nedan.

Teckensekvensen matchar samma sekvens i inmatningssträngen, så mönstret bluh matchar delsträngen bluh i inmatningssträngen. Så långt så lätt, eller hur?

Om du vill att metatecken eller escape-sekvenser ska behandlas som vanliga tecken måste de föregås av \ , till exempel matchar metatecken ^ vanligtvis början av rader, men om den skrivs som \^ , kommer den att matcha tecknet ^, \ \ matchar med \ etc.

Exempel:

foobar hittar 'foobar' \^FooBarPtr hittar '^FooBarPtr'

Escape-sekvenser

Alla tecken kan escapes precis som i C eller Perl: \n betyder början på en rad, \t betyder en tabb osv. I allmänhet betyder \xnn , där nn är en sekvens av hexadecimala siffror, tecken med ASCII kod nn. Om du behöver ange ett dubbelbyte-tecken (Unicode), använd formatet \x(nnnn) , där nnnn är en eller flera hexadecimala siffror.

\xnn tecken med hexadecimal kod nn \x(nnnn) tecken med hexadecimal kod nnnn (mer än en byte kan endast anges i läge (tregexpr_interface.html#unicode))| \t tab (HT/TAB), kan också \x09 \n nylinje (NL), kan också \x0a \r vagnretur (CR), kan också \x0d| \f formatöversättning (FF), \x0c| \a call (BEL), \x07| \e escape (ESC), \x1b|

Exempel:

foo\x20bar matchar 'foo bar' (notera utrymmet i mitten) \tfoobar matchar 'foobar' föregås av en flik

Symbollistor

Du kan definiera en lista genom att omsluta tecknen i . Listan kommer att matcha alla tecken i den.

Om det första tecknet i listan (direkt efter [) är ^, matchar listan alla tecken som inte finns med i listan.

Exempel:

foobr matchar 'foobar', 'foober' osv. men inte 'foobbr', 'foobcr' osv. foob[^aeiou]r matchar 'foobbr', 'foobcr' etc. men inte 'foobar', 'foober' etc.

Inom en lista kan tecknet - användas för att definiera teckenintervall, till exempel representerar a-z alla tecken mellan a och z inklusive.

Om du behöver inkludera tecknet - sig självt i listan, placera det i början eller slutet av listan, eller prefix det med en \ . Om du behöver lägga in själva tecknet ] i listan, sätt det i början eller prefix det med en \ .

Exempel:

[-az] "a", "z" och "-", "a", "z" och "-", "a", "z" och "-" alla 26 små latinska bokstäver från "a" till "z" [\n-\x0D] #10, #11, #12, #13. [\d-t] siffra, "-" eller "t". -a] tecken från intervallet "]".."a".

Metakaraktärer

Metakaraktärer är Specialsymboler, vilket är det viktigaste begreppet i reguljära uttryck. Det finns flera grupper av metakaraktärer.

Metatecken - radavgränsare

^ början av raden $ slutet av raden \A början av text \Z slutet av texten. valfritt tecken i strängen

Exempel:

^foobar hittar bara "foobar" om den hittar "foobar" i början av strängen foobar$ bara om den hittar "foobar" i slutet av strängen ^foobar$ endast om det är det enda ordet i strängen foobar.r hittar "foobar", "foobbr", "foob1r" osv.

Metateckenet ^ matchar som standard endast i början av inmatningstexten, och metatecken $ matchar endast i slutet av texten. Interna radavgränsare som finns i text kommer inte att matcha ^ och $ .

Men om du behöver behandla text som flera rader så att ^ matchar efter varje radavgränsare i texten, och $ före varje avgränsare, kan du inkludera /m-modifieraren.

Metartecknen \A och \Z liknar ^ och $ , men de påverkas inte av /m-modifieraren, dvs. de matchar alltid bara början och slutet av hela inmatningstexten.

Metakaraktär. som standard matchar alla tecken, men om du stänger av /s-modifieraren, då. kommer inte att matcha radavgränsare.

TRegExpr tolkar radavgränsare som rekommenderas av www.unicode.org:

^ matchar början av inmatningstexten, och även, om /m-modifieraren är aktiverad, perioden omedelbart efter \x0D\x0A , \x0A eller \x0D (om du använder Unicode-versionen av

$ matchar slutet av inmatningstexten, och även, om /m-modifieraren är aktiverad, punkten omedelbart före \x0D\x0A , \x0A eller \x0D (om du använder Unicode-versionen av TRegExpr, även \x2028 eller \ x2029 eller \x0B eller \x0C eller \x85). Observera att det inte matchar mellanrummet inuti \x0D\x0A-sekvensen.

Matchar vilket tecken som helst, men om r /s-modifieraren är avstängd, då. matchar inte \x0D\x0A och \x0A och \x0D (om du använder Unicode-versionen av TRegExpr matchar den inte heller \x2028 och \x2029 och \x0B och \x0C och \x85).

Observera att ^.*$ (mönster för en tom sträng) inte matchar en tom sträng av formen \x0D\x0A , men matchar \x0A\x0D .

Du kan åsidosätta ovanstående beteende när du bearbetar flerradstexter - se beskrivningarna av egenskaperna LineSeparators och LinePairedSeparator, till exempel kan du åsidosätta att endast använda Unix-radseparatorer \n eller endast DOS/Windows-separatorer \r\n eller blandade separatorer (och konfigurerad som standard) eller till och med definiera dina egna radavgränsare!

Metatecken - standardlistor med tecken

\w alfanumeriskt tecken eller "_" \W inte \w \d numeriskt tecken \D inte \d \s något "blanksteg"-tecken (standard är [ \t\n\r\f]) \S inte \ s

Standardlistorna \w , \d och \s kan också användas i teckenlistor.

Exempel:

foob\dr matchar "foob1r", ""foob6r" etc. men inte "foobar", "foobbr" etc. foob[\w\s]r matchar "foobar", "foob r", "foobbr" etc. men inte "foob1r", "foob=r" osv.

Metatecken - varianter

Du kan definiera en lista med alternativ med hjälp av metatecken | för att skilja dem åt, till exempel kommer fee|fie|foe att matcha fee eller fie eller foe , (samma som f(e|i|o)e). Som det första alternativet uppfattas allt från föregående metatecken (eller [ eller från början av uttrycket till första metatecken | , som sista - allt från sista | till slutet av uttrycket eller till närmaste metatecken). Vanligtvis, för att inte bli förvirrad, är en uppsättning alternativ alltid omgiven inom parentes, även om detta skulle kunna undvaras.

Alternativen prövas med början från det första och försöken slutförs så snart det är möjligt att välja ett där hela efterföljande del av uttrycket matchar (för mer information, se arbetsmekanismen). Detta innebär att alternativ inte nödvändigtvis ger girigt beteende. Om du till exempel använder uttrycket foo|foot på inmatningssträngen barefoot , kommer foo att hittas eftersom det är det första valet som gjorde att hela uttrycket kunde matcha.

Observera att metatecken | behandlas som ett vanligt tecken i teckenlistor, till exempel betyder det exakt detsamma som .

Exempel:

foo(bar|foo) matchar "foobar" eller "foofoo".

Metatecken - underuttryck

Metatecken (...) kan också användas för att specificera underuttryck

  • när sökningen efter ett uttryck är klar kan du hänvisa till vilket underuttryck som helst med hjälp av egenskaperna MatchPos, MatchLen och Match, och även ersätta underuttryck i en mall med Substitute-metoden).

Underuttryck numreras från vänster till höger, i den ordning som de inledande parenteserna visas.

Det första deluttrycket har nummer 1 (uttrycket som helhet är 0" , det kan nås i Substitute som $0" eller $&).

Exempel:

(foobar)(8,10) hittar en sträng som innehåller 8, 9 eller 10 kopior av "foobar" foob(|a+)r hittar "foob0r", "foob1r" , "foobar", "foobaar", "foobaar" osv.

Metatecken - Bakåtreferenser

Metatecken \1 till \9 behandlas som bakåtreferenser. \ matchar det tidigare hittade underuttrycket # .

Exempel:

(.)\1+ matchar "aaaa" och "cc". (.+)\1+ matchar också "abab" och "123123" ([""]?)(\d+)\1 matchar "13" (i dubbla citattecken), eller "4" (i enkla citattecken) eller 77 (utan citattecken) osv.

Modifierare

Modifierare används för att ändra driftsätten för TRegExpr.

Du kan ändra modifierare på flera sätt.

Alla modifierare kan ändras med en speciell konstruktion (?...) inuti det reguljära uttrycket.

Du kan också tilldela ett värde till motsvarande egenskap för TRegExpr-objektinstansen (till exempel ModifierX för att ändra /x-modifieraren eller ModifierStr för att ändra flera modifierare samtidigt). Standardvärden för nya instanser av TRegExpr-objekt definieras i , till exempel definierar RegExprModifierX standardvärdet för ModifierX.

i

Skiftlägesokänsligt läge (som standard använder det standardspråket som valts i operativsystemet), (se även InvertCase)

m

s

g

Inte en standardmodifierare. Genom att stänga av den växlar du alla repeatrar till läget "icke girigt" (denna modifierare är aktiverad som standard). De där. om du stänger av den fungerar alla + som +? , * hur *? etc.

x

Låter dig formatera mallen för att göra den lättare att läsa (se beskrivning nedan).

r

Inte en standardmodifierare. Om aktiverat, då intervallen typ a-z inkluderar även bokstaven ё, A-Ya inkluderar Yo och a-Ya inkluderar alla ryska bokstäver i allmänhet.

/x-modifieraren får TRegExpr att ignorera mellanslag, tabbar och radavgränsare, vilket gör att du kan formatera uttryckets text. Dessutom, om tecknet # påträffas, uppfattas alla efterföljande tecken fram till slutet av raden som en kommentar, till exempel:

((abc) # Kommentar 1 | # Mellanslag i ett uttryck ignoreras också (efg) # Kommentar 2)

Naturligtvis betyder detta att om du behöver infoga ett mellanslag, tabb eller radavgränsare eller # i ett uttryck, så kan detta i utökat (/x) läge endast göras genom att prefixet dem med / eller använda /xnn (inuti teckenlistor , alla dessa karaktärer behandlas som vanligt)

Perl-tillägg

(?imsxr-imsxr)

Låter dig ändra modifieringsvärden

Exempel:

(?i)Saint-Petersburg hittar "Sankt-Petersburg" och "Sankt-Petersburg" (?i)Sankt-(?-i)Petersburg hittar "Sankt-Petersburg" men inte "Sankt-Petersburg" (?i)(Sankt) -)?Petersburg hittar "Sankt-Petersburg" och "sankt-petersburg" ((?i)Sankt-)?Petersburg hittar "sankt-Petersburg" men inte "sankt-petersburg"

Hemligheter för reguljära uttryck (reguljära uttryck)

Del 1. Dialekter och möjligheter. Att komponera reguljära uttryck

Innehållsserie:

1. Introduktion. Använder vi reguljära uttryck till sin fulla potential?

Om du tänker på frågan: "Vad är ett "vanligt uttryck" i allmänhet?", kommer svaret inte att hittas omedelbart. Vi kan säga att detta är ett specialiserat språk för att beskriva ett symboliskt mönster (teckensekvens) för sökning i textrader. Det viktiga här är att när man söker efter matchningar så är det en jämförelse karaktär för tecken som utförs. Författare till uppslagsverket vanliga uttryck(Bemästra reguljära uttryck) J.E.F. Friedl rekommenderar att du utvecklar vanan att tolka reguljära uttryck bokstavligt. Om man till exempel tittar på mönstret "^cat", som betyder "strängen måste börja med ordet katt", bör man resonera så här: "en matchning kommer att hittas om vi är i början av strängen och hittar tecknet c, omedelbart följt av tecknet a, omedelbart efter vilket är symbolen t". Detta låter dig noggrant utvärdera innebörden och essensen av det reguljära uttrycket.

De flesta användare vet att det räcker med att ange ett mönsterord för att söka. Till exempel, i en webbläsare, i "Sök"-fältet, efter att ha angett "Linux", kommer du att få en lång lista med länkar till sidor som matchar det givna mönstret "Linux" i texten. I lokal filsystem grep "Linux" används, eller grafiska hjälpmedel Sök.

Inte alla, men många användare vet hur man använder metatecken (* . ?) i sökmönster. Ännu färre är medvetna om möjligheten att använda modifierare och andra sofistikerade verktyg för att konstruera reguljära uttryck, d.v.s. i många fall är kraften hos motorn för reguljära uttryck knappt en tredjedel använd. Varför inte försöka öka effektiviteten?

2. Olika dialekter av reguljära uttryck. POSIX-överensstämmelse

Generellt sett finns det två huvuddialekter (eller typer) av reguljära uttryck: enkla och utökade. Samtidigt är gränsen mellan dem villkorad och blir mindre och mindre tydlig med tiden.

Programmen vi(m), sed, grep, less, ed, expr, lex förstår bara enkla reguljära uttryck, medan verktygen (g)awk, egrep och tolkar Perl-språk, Tcl, Python - utökade reguljära uttryck. Samtidigt har vart och ett av programmen sina egna förbättringar, d.v.s. subdialekter av reguljära uttryck skapas. Tänk på likheterna och skillnaderna mellan dessa dialekter.

2.1. Allmänt schema för ett reguljärt uttryck

Som regel består ett reguljärt uttryck av tre huvuddelar:

  1. Ankare - definierar mallens position i en textrad:
    • ^ - ankare som definierar början av linjen;
    • $ är ett ankare som definierar slutet av strängen.
  2. En uppsättning (sekvens) av tecken - för att söka efter matchningar i givna positioner i en textsträng:
    • punkttecknet (.) matchar alla godtyckliga tecken;
    • alfanumeriska tecken och mellanslag representerar sig själva;
    • andra tecken - tolkningen beror på dialekten.
  3. Modifierare - ställer in antalet repetitioner av föregående tecken eller uppsättning tecken (beroende på dialekten):
    • * – valfritt antal tecken/upprepningar, inklusive noll;
    • ? – matchar noll eller en instans av ett tecken/uppsättning;
    • + - matchar en eller flera tecken/uppsättningsinstanser.

Exempel: du måste hitta alla i källkod på språket S.

grep "^ *#define.*" *.c *.h

Här tas hänsyn till att valfritt antal mellanslag kan infogas i början av makrodefinitionsraden, eller så finns det inga mellanslag. #define-delen av mallen är bokstavlig, dvs. varje tecken tolkas "som den är". Den sista delen av mönstret betyder "alla tecken i valfri mängd".

Observera att tecknet ^ endast tolkas som ett ankare som anger början av en rad om det är det allra första tecknet i mönstret. På liknande sätt markerar symbolen $ slutet på en rad, förutsatt att det är det allra sista tecknet i mönstret. I alla andra fall blir dessa tecken bokstavliga, d.v.s. representera sig själva.

2.2. Definiera teckenintervall i reguljära uttryck

Om det blir nödvändigt att ange ett tecken från en viss grupp, till exempel endast ett numeriskt tecken, eller endast en gemen vokal, eller endast skiljetecken, används hakparenteser, inom vilka de nödvändiga tecknen definieras. På det här sättet:

  • – matchar ett numeriskt tecken från den angivna uppsättningen;
  • [aeyiouyeyuya] - motsvarar en av de listade vokalerna;
  • [,.:;] – matchar ett av skiljetecken.

Observera att i det senare fallet förlorar perioden inom hakparenteser sin speciella status och betyder inte "vilket tecken som helst", utan själva tecknet "prick".

Kontinuerliga teckenintervall kan förkortas med ett bindestreck: det första exemplet skrivs mer bekvämt som . Dessutom är alla kombinationer av intervall och specifika tecken tillåtna.

Det är också möjligt att utesluta specificerade teckenuppsättningar från sökningen, vilket görs på följande sätt:

  • [^0-9] – matchar alla tecken utom numeriska;
  • [^aeyiouyeyyuya] - matchar valfri INTE-vokal.

Vi kommer att bekanta oss med andra nyanser av att definiera teckenintervall inom hakparenteser när vi tillämpar dem, och nu kommer vi att överväga modifierare med hjälp av exemplet med ett digitalt sökmönster för IP-adresser.

2.3. Modifierare av antalet teckenupprepningar

Här ligger svårigheten i det faktum att *-modifieraren inte är lämplig för att hitta en IP-adress - ett försök att använda mönstret *\.*\.*\. kommer att resultera i rader som innehåller element som 2344.5657.11.00000 som inte är IP-adresser. För att ange antalet repetitioner av teckenuppsättningar används modifieraren \(min,max\). Att veta att varje del av en IP-adress kan innehålla från en till tre siffror, skriver vi modifieraren som \(1,3\). "Omvänt snedstreck" före punkter behövs för att åsidosätta deras speciella universella metateckenstatus. Observera också att värdet 0 inte används som den första byten av vanliga IP-adresser. Som ett resultat får vi följande sökmönster:

grep "\(0,2\)\.\(1,3\)\.\(1,3\)\.\(1,3\)" *.txt

Modifieraren \(min,max\) fungerar bara på enkla reguljära uttryck. Utökade reguljära uttryck kan inte använda \( \), men kan använda modifieraren? som motsvarighet till uttrycket \(0,1\), och +-modifieraren som motsvarighet till uttrycket \(1,\). I det andra fallet finns det inget numeriskt värde efter decimalkomma, vilket innebär att det maximala antalet matchningar inte är begränsat.

2.4. Att komma ihåg och återanvända ett mallelement

Denna mekanism fungerar också endast i enkla reguljära uttryck. (Men på språk Perl programmering, Python, etc. denna mekanism bibehålls - gränsen mellan dialekter blir mindre och mindre urskiljbar, minns du?)

I enkla reguljära uttryck kommer delar av mönstret som är inneslutna i \(\)-konstruktionen ihåg och numreras, varefter de kan återanvändas. Upp till nio numrerade mönster kan memoreras totalt. Det mest illustrativa exemplet på hur man använder memoreringsmekanismen är sökningen efter palindromer (ord som läses på samma sätt både från vänster till höger och från höger till vänster):

  • \(\)\(\)\2\1 - för fembokstavspalindromer (t.ex. nivå, rotor, fru, etc.)
  • \(\)\(\)\(\)\3\2\1 - för palindromer med sex bokstäver (t.ex. rödare, succus, terret, etc.)

2.5. POSIX-överensstämmelse

POSIX-standarden delar också in reguljära uttryck i två kategorier: BRE (Basic Regular Expressions) och ERE (Extended Regular Expressions). Metatecken stöds i båda kategorierna. och *, ankare ^ och $, gruppering av tecken inom parentes (för BRE, parentes escapes med snedstreck), tillämpar \(min,max\) kvantifierare på grupper inom parentes. Lagring och återanvändning av \1...\9 stöder endast BRE-kategorin, medan kvantifierarna + och? och urvalskonstruktion - endast ERE-kategori.

V POSIX standard begreppet lokal kontext (locale) används - en uppsättning parametrar som beskriver språkliga och kulturella regler: datum- och tidsformat, tolkning av aktiva kodningstecken, etc. Detta är inte direkt relaterat till reguljära uttryck, men det påverkar hur de fungerar. När man arbetar i ett lokalt sammanhang med UTF-8-kodning, som accepteras i nästan alla moderna distributioner, bearbetas tecken i det ryska alfabetet och deras intervall korrekt, d.v.s. du kan ange intervallen [a-z] och [a-z] i sökmönster.

3. Exempel på att komponera användbara reguljära uttryck

För att skapa korrekt fungerande reguljära uttryck räcker det inte med en teori. Det är nödvändigt att lära sig att inte bara konstruera och skriva en mall, utan också att ta full hänsyn till det sammanhang i vilket den kommer att jämföras. Att skriva och förbättra mallen är en iterativ process, under vilken två huvuduppgifter löses: å ena sidan få alla nödvändiga rader, inte hoppa över de som enligt planen borde ha matchat, men av någon anledning inte matchade ; å andra sidan, uteslut alla onödiga linjer, inklusive de som designmässigt borde kasseras, men av någon anledning matchas.

3.1. Ett exempel på en mall för att söka efter en summa pengar skriven i formatet "10 000 rubel. 00 kopek."

\(1,\) gnugga\. \(2\)polis\.

Nödvändigt förtydligande: om typmodifieraren \(min,max\) inte innehåller både ett kommatecken och ett maximalt värde, så anger denna konstruktion det exakta antalet förväntade upprepningar av mallelementet. I vårt exempel är exakt två numeriska tecken definierade för att beteckna kopek.

3.2. Ett exempelmönster för att söka efter en URL-sträng som motsvarar en webbresurs på Internet:

http://\(1,\)\.[-a-zA-Z0-9_]\(1,\)/*

Nödvändig förklaring: bindestrecket tappar sin speciell betydelse om det anges i den allra första positionen omedelbart efter den öppna hakparentesen i intervallet. Det här mönstret kan också matcha "exotiska" URL-strängar som http://my.home-server/

I utökat reguljärt uttrycksformat skulle det här mönstret kunna skrivas mer kompakt:

http://+\.[-a-zA-Z0-9_]+/*

En sådan post förstås till exempel av verktygen egrep och awk.

3.3. Mallen för att söka efter valfri HTML-tagg ser förvånansvärt enkel ut:

<[^>]+>

Matchar vilken teckensekvens som helst förutom en eller flera > inom vinkelparenteser. Med andra ord kommer en tagg med ett tecken också att hittas

Och mer utförliga taggar som


.

3.4. Alternativ för mall för datumsökning

Utökade reguljära uttryck låter dig skriva ett något besvärligt, men ändå korrekt fungerande mönster för att hitta datum som ser ut som "13 november 2009":

? (Jan|Feb|Mar|Apr|Maj|Juni|Juli|Aug|Sep|Okt|Nov|Dec).* y\.

Nackdelen med detta mönster är att det inte kan hitta datum från antikens historia, till exempel "13 november 245." eller 1 januari 88", men det är ganska lämpligt för att arbeta med moderna dokument (vi tar hänsyn till sökkontexten!).

3.5. Praktisk tillämpning av de numrerade delarna av mönstret

I föregående avsnitt gav jag redan ett exempel på ett mönster för att hitta palindromer. Dess funktionalitet kan också förbättras något genom att skriva om uttrycket enligt följande:

\(.\)\(.\)\(.\)\3\2\1

Med den här mallen kan du hitta palindromer med sex tecken inte bara på engelska utan också på ryska och andra språk, samt sekvenser av icke-alfabetiska tecken, till exempel /*!!*/

Ett mer praktiskt sätt att använda de memorerade och numrerade delarna av en mall är att söka efter upprepade ord som står bredvid varandra, vilket gör att du kan upptäcka sådana vanliga fel (stavfel) i texter som "för för". Mallen kan skrivas så här:

\<\(..*\)\> \<\1\>

Ytterligare två element av reguljära uttryck används här: \< для обозначения начальной границы слова и \>för att ange den sista ordgränsen. Alltså kommer vi bara ihåg enskilda ord, inte några teckensekvenser. Uttrycket ..* matchar alla ord som består av åtminstone från en karaktär. Som ett resultat kommer vi att kunna hitta upprepningar som "och och", "inte inte", "för för" osv.

3.6. Begränsning av storleken på den matchande delen av mönstret

En annan egenskap hos reguljära uttrycks "karaktär" är att de är otroligt "giriga" (giriga), d.v.s. sträva efter att matcha så nära som möjligt lång kö. På grund av denna "girighet" kan oväntade problem uppstå. Till exempel finns det ett mönster för att söka efter valfritt antal tecken inom citattecken:

".*"

Raderna som ska sökas är följande:

"Petrov" "vakt" "Ivanov" "försörjningsavdelning" "speditör" "Sidorov" "administration" "direktör"

Om uppgiften var inställd för att bara extrahera det första argumentet från de givna strängarna (anställdas efternamn), kommer mallen som föreslagits ovan att utföra det felaktigt, eftersom det andra citatet i mallen matchar det sista citatet i strängen (på grund av önskan för att få maximal matchning). Malländring:

".*" ".*"

löser problemet endast för den första raden, och i den andra och tredje är arbetsplatsen också kopplad till efternamnet - igen, inte vad vi behöver!

Den här uppgiften löses korrekt med ett reguljärt uttryck som matchar det kortaste av alla möjliga fragment av strängen som finns mellan två citattecken:

"[^"]*"

Här måste det inledande citatet följas av valfritt antal tecken utan citattecken tills det avslutande citatet påträffas.

4. Slutsats

Även från dessa exempel, långt ifrån de mest komplexa som beskrevs i den här artikeln, kan du förstå hur rika och mångsidiga möjligheterna med reguljära uttryck är. Du kan till och med läsa inspelningsformatet för deras mallar egendomligt språk programmering, efter att ha lärt sig att tänka och skriva på vilket, kommer du att rädda dig från ett stort antal monotont och tråkigt arbete.

Den första artikeln gav allmän uppfattning om reguljära uttryck och deras omfattning, samt kort recension egenskaper hos deras dialekter. Exempel på att komponera reguljära uttryck för att lösa olika problem övervägdes.

Fortsättningen av cykeln kommer att ägnas åt praktiskt arbete med reguljära uttryck i specifika program och språkmiljöer.

Ladda ner resurser

static.content.url=http://www.website/developerworks/js/artrating/

Artikel-ID=487264

ArticleTitle=Hemligheter för reguljära uttryck (reguljära uttryck): Del 1. Dialekter och funktioner. Att komponera reguljära uttryck

Jag bestämde mig för att skriva ett fuskblad om vanliga uttryck. Kanske glömmer jag dem en dag. Det här inlägget kan också betraktas som en fortsättning på min serie av Perl-tutorials.

1. Introduktion

Några ord för den som inte riktigt vet vad i fråga. Har du någonsin sett filnamnsmasker - alla sorters *.html, filnamn.(txt|csv) och så vidare? Så, reguljära uttryck är samma "masker", bara mer komplexa. I rätt händer kan reguljära uttryck vara otroligt kraftfulla. På ett eller annat sätt används de i 95 % av mina manus.

Många tror med rätta att reguljära uttryck mer är ett självständigt programmeringsspråk än en del av något språk. Reguljära uttryck finns i Perl, PHP, Python, JavaScript, config Apache-filer… Beroende på språket kan det finnas små skillnader i syntaxen för reguljära uttryck, men grundidéerna är desamma överallt.

Därför, trots att alla exempel i anteckningen är skrivna i Perl, kommer den information som tillhandahålls också att vara användbar för programmerare som använder något annat språk i sitt arbete. Till exempel denna PHP-kod:

if (preg_match("//" , $text ) ) (
// det finns siffror i texten
) annat (
// det finns inga siffror i texten
}

och den här i Perl:

if ($text =~ // ) (
# det finns siffror i texten
) annat (

}

gör samma sak. Eftersom det inte är svårt att gissa från kommentarerna i koden, kontrolleras här om $text-strängen innehåller minst en siffra.

2. Enkla exempel

Som alltid kommer vi att lära av exempel. Hakparentes i reguljära uttryck betyder "det måste finnas ett av de listade tecknen." Till exempel uttrycket ovan matchar en sträng som innehåller minst en siffra. Likaså uttrycket matchar en sträng som innehåller minst en av de tre första bokstäverna i det latinska alfabetet. För att representera vilken karaktär som helst, Förutom specificeras används posten [^abcdef], det vill säga med keps symbol omedelbart efter den öppna hakparentesen.

Anta att vi måste kontrollera om en sträng innehåller något tecken i det latinska alfabetet. Att lista alla 26 bokstäverna är inte särskilt bekvämt, eller hur? Speciellt för sådana fall i reguljära uttryck kan du använda streck inom hakparenteser för att beteckna en ordnad uppsättning tecken. Uttryck alla strängar som innehåller minst en liten engelsk bokstav kommer att matcha. I analogi kan det föregående exemplet med siffror skrivas mer kortfattat:

if ($text =~ // ) (
# det finns siffror i texten
) annat (
# det finns inga siffror i texten
}

Och ett par exempel till:

if ($text =~ // ) (
# texten innehåller siffror och/eller gemener
# matchningar: abc, ZZaZZ, ===17
# matchar inte: EPIC FAIL, @^* [e-postskyddad]#
}

if ($text =~ /[^0-9]/ ) (
# texten innehåller andra tecken än siffror
# matchningar: abc, 123abc456, 0x1111111111
# matchar inte: 123, 123456, 9999999999
}

if ($text =~ // ) (
# texten innehåller bokstäver i det latinska alfabetet
# passar: ___Abba___, zyx
# inte lämplig: 0123, ^_^
}

if ($text =~ // ) (
# text innehåller siffror och bokstäver från A till F
# passar: ***777***, DeadC0de, intel, 0_o
# passar inte: Xor, wiki
}

Låt oss komplicera uppgiften. Nu måste vi kontrollera inte bara närvaron eller frånvaron av vissa tecken, utan strängens överensstämmelse med ett visst format. Här är några enkla exempel:

if ($text =~ /num=/ ) (
# passar: num=1, some_num=000, bebenum=2(&^*
# inte lämplig: NUM=1, my_num=-1, num=abc
}

if ($text =~ / / ) {
# passar:
#zzz zzz
#
# matchar inte:
#
#
}

Den uppmärksamma läsaren kommer att undra vad det är plustecken står i det sista reguljära uttrycket? Detta tecken betyder "en eller flera av tecknen som listas före detta plus". Nästan samma symbol betyder stjärna"från noll- upp till så många tecken du vill före asterisken." Till exempel uttrycket A+ kommer att matcha en sekvens av ett eller flera tecken A och uttrycket * - valfritt antal siffror, inklusive ingen.

Ibland måste antalet tecken specificeras mer exakt. Detta kan göras med lockiga hängslen . Till exempel uttrycket {8} matchar valfri sekvens med exakt åtta siffror och uttrycket {3,8} — en sekvens som innehåller från 3 till 8 tecken i det latinska alfabetet.

Siffran i den andra positionen kan utelämnas. Det är uttrycket {3,} kan också äga rum. Det betyder "minst tre gemener latinska alfabetet." Uttryck {0,} helt lik en asterisk, och {1,} - ett plus. Uttryck {0,1} kan skrivas mer kortfattat med hjälp av frågetecken.

Ett exempel (inte det lättaste, men intressant):

if ($text =~ // ) {
# passar:
#dfgd dfgdfg
#
# matchar inte:
#
#
}

Om det här exemplet får din hjärna att koka är det dags att träna lite reguljära uttryck genom att skriva testprogram. Annars, från vidare läsning kommer du att få en enda röra i ditt huvud. Om allt är klart än så länge, låt oss gå vidare.

3. Hur river man ut en bit snöre?

Symbol vertikal stång(aka "pipa" eller bara "stick") betyder "eller" i reguljära uttryck. Till exempel uttrycket {20}|{25} matcha alla strängar som innehåller 20 latinska tecken eller 25 siffror i rad. Detta tecken används vanligtvis tillsammans med parentes, utformad för att gruppera delar av ett reguljärt uttryck. Exempel:

if ($filnamn =~ /backup(19|20)(2)-(2)-(2)/) {
# lämplig: backup2011-04-01, backup1999-01-13
# matchar inte: backup1873-12-12, backup2101-07-07
}

Parenteser har en annan funktion. Med deras hjälp kan du riva ut bitar av motsvarande linjer. I PHP lagras resultatet i variabeln som anges som det tredje argumentet till preg_match-funktionen. I Perl lagras matchningar för det 1:a, 2:a ... 9:e paret av parenteser i variablerna $1, $2, ..., $9 . Men det är bekvämare att använda denna konstruktion:

if (my ($y , $m , $d ) =
$filnamn =~ /backup((4))-((2))-((2))/) {
skriva ut;
}

Frågan är, under vilket nummer man ska leta efter en matchning i den returnerade arrayen, om det reguljära uttrycket innehåller kapslade parentes? Det är enkelt - tändstickor returneras i samma ordning som öppningsparenteserna. Exempel:

mitt $filnamn = "./dumps/backup2011-04-01.tgz";
$filnamn =~ /backup((20|19)(2))-((2))-((2))/;
skriv ut "$1, $2, $3, $4 \n";
# kommer att matas ut: 2011, 20, 04, 01

Ibland skulle vi vilja gruppera någon del av ett uttryck, men inte returnera det. För att göra detta, omedelbart efter öppningsparentesen, måste du skriva sekvens av frågetecken och kolon. Exempel:

if (my ($y , $m , $d ) =
$filnamn =~ /backup((?:20|19)(2))-((2))-((2))/) {
skriva ut "år = $y, månad = $m, dag = $d\n";
}

Det kan också följas av parentes frågetecken, plus eller asterisk, som anger att konstruktionen inom parentes är valfri, måste upprepas 1+ gånger respektive måste upprepas 0+ gånger. Att använda lockiga hängslen efter parentes är också acceptabelt.

4. Början och slutet av raden

Det är ofta användbart att ange i ett reguljärt uttryck var en sträng ska börja och/eller sluta. Den första är klar med keps symbol i början av uttrycket, den andra - med hjälp av dollartecken i slutet. Exempel:

if ($text =~ /^*/ ) (
# text som börjar med decimalsiffra
# passar: 3, 801403, 6543bebebe
# matchar inte: 0275, -123, abc11111
}

if ($text =~ /^0x(1,8)$/ ) (
# hexadecimalt tal i C-notation
# lämplig: 0x5f3759df, 0xDEADBEEF
# matchar inte: 0x1234xxx, xxx0x5678, xxx0x9ABCxxx
}

Inte svårt, eller hur? Observera att när du kontrollerar webbformulärfält, funktionsargument innan du ersätter dem i en SQL-fråga, och så vidare, nödvändigtvis bör kontrolleras Allt sträng, som gjordes i det senaste reguljära uttrycket.

Notera: Om någon undrar vad detta är magiska siffror» 0x5f3759df och 0xDEADBEEF , se Wikipedia.

5. Specialtecken

Förutom de namngivna specialtecknen bör det också noteras punkt. Hon menar några annan karaktär än karaktär ny linje. Användningsexempel:

if (my ($name ) = $arg =~ /^--name=(.+)$/ ) (
print "Hej, $name! \n";
}

Som standard producerar reguljära uttryck vad som kallas girig analys. Söker med andra ord matchningar maximal längd. När vi använder en prick kan detta vara problematiskt. Till exempel måste vi riva ut lite text från hundratals HTML-sidor med något sånt här:

<span > Text<em > text</em> text</span> Källa: http://webbplats/</span>

Följande kod kommer inte att returnera det vi vill ha:

# det reguljära uttrycket innehåller ett snedstreck, alltså
# måste använda en annan avgränsare istället
(.*)#;
skriv ut $text;
# kommer att mata ut den längsta matchningen:
# text text textKälla: http://webbplats/

Och här är vad som händer om du stänger av girig analys (uppmärksamhet på frågetecknet):

my($text ) = $data =~ m # (.*?)#;
skriv ut $text;
# skriver ut den första matchningen:
# text text text

Ja, följande rader gör samma sak:

# vanlig notation...
$text =~ /({4})-({2})-({2})/ ;
# är egentligen bara en förkortning för m//
$text =~ m/((4))-((2))-((2))/;
# istället för ett snedstreck kan du använda olika parenteser:
$text =~ m (([0-9] (4)) - ([0-9] (2)) - ([0-9] (2)));
$text =~m< ([ 0 - 9 ] { 4 } ) - ([ 0 - 9 ] { 2 } ) - ([ 0 - 9 ] { 2 } ) >;
$text =~ m [([0-9] (4)) - ([0-9] (2)) - ([0-9] (2))];
$text =~ m (([0-9] (4)) - ([0-9] (2)) - ([0-9] (2)));
# eller till och med tecken som detta:
$text =~ m ! ([0-9] (4)) - ([0-9] (2)) - ([0-9] (2))!;
$text =~ m | ([0-9] (4)) - ([0-9] (2)) - ([0-9] (2)) |;
$text =~m #({4})-({2})-({2})#;
# och cap, citattecken, kolon, kommatecken, punkt, ...

Varför finns det så många sätt att skriva reguljära uttryck? Föreställ dig att uttrycket innehåller snedstreck, punkter, kommatecken och andra tecken, men inte innehåller ett utropstecken. Sedan kan vi naturligtvis inte använda snedstreck, punkter och så vidare för att indikera början och slutet av det reguljära uttrycket, men Utropstecken- Kan.

Ofta i reguljära uttryck måste man använda snedstreck. Placerad före en prick, parentes, plus, cap och andra symboler betyder det "nästa symbol betyder exakt symbolen, inte något annat." Så här kan du till exempel bestämma filtillägget efter dess namn:

# omvänt snedstreck escaped punkt
# betyder exakt en prick, inte "vilket tecken som helst"
my ($ext ) = $fname =~ /\.(+)$/ ;
skriva ut "filnamn: $fname, tillägg: $ext\n";

Dessutom används omvänt snedstreck i följande notation:

  • \t- anger ett tabbtecken ( t ab)
  • \r och \n— vagnreturtecken ( r eturn) och en ny rad ( n ewline)
  • \xNN— matchar tecknet med ASCII-koden NN, till exempel \x41 motsvarar stor bokstav A av det latinska alfabetet
  • \s- matchar ett mellanslag ( s tempo), tabb, nylinje eller vagnretur
  • \d- betyder vilket nummer som helst ( d igit), eller snarare, vad som anses vara en Unicode-siffra (se bildnummer 102 i denna presentation)
  • \w- betyder det så kallade "ordet" ( w ord), analog

I de tre sista uttrycken betyder att skriva en bokstav med versaler negation. Till exempel, \D matchar uttrycket [^0-9] , \W- uttryck [^0-9a-zA-Z_], a \S- alla tecken som inte är blanksteg.

Alla dessa "bokstavliga" uttryck kan användas inuti hakparentes. Till exempel uttrycket helt likvärdig .

Uttrycken förtjänar särskild uppmärksamhet. \b och \B, vilket betyder ordets gräns (i samma betydelse av ordet, som i fallet med \w) respektive frånvaron av en ordgräns. Till exempel uttrycket perl\b matchar "perl rulez!" men inte "perlmonk". Med uttryck perl\B allt är precis tvärtom. Jag hoppas att tanken är tydlig.

Och ytterligare ett exempel:

# dela fullständiga namn fil till sökväg och namn
min ($sökväg , $fname ) = $fullständigt_namn =~ /^(.*)\/([^\/]+)$/ ;

Det illustrerar användningen av ett omvänt snedstreck för att undkomma ett tecken, som används för att markera gränserna för ett reguljärt uttryck. V detta exempel detta är ett direkt snedstreck.

6. Modifierare

Beteendet för reguljära uttryck kan ändras med hjälp av modifierare. Till exempel, som du kanske har märkt, kontrolleras en strängs matchning mot ett reguljärt uttryck på ett skiftlägeskänsligt sätt. Du kan ändra detta beteende med modifieraren # (.*?)#g;
# var försiktig när du använder /g i skalärt sammanhang
# detaljer här: http://koorchik.blogspot.com/2011/07/perl-5.html
skriv ut "$_ \n" för (@ord) ;

Som nämnts ovan står en punkt för vilken karaktär som helst, förutom nyradstecken. Du kan ändra detta beteende med modifieraren /s:

# riva ut innehållet i artikeln från HTML-filen,
# som kan innehålla långt ifrån en eller två rader
min($artikel) = $html =~m #

(.*?)
#s;

Förresten, om du behöver ange "vilket tecken som helst" i ett reguljärt uttryck utan att använda en modifierare /s, använd uttrycket [\d\D]. Det betyder "vilket tecken som helst som är en siffra eller inte en siffra", det vill säga vilket tecken som helst i allmänhet.

Slutligen, ingenting hindrar dig från att använda flera modifierare samtidigt:

# riv ut allt i fet stil från HTML-filen
mina @ord = $html =~ m # (.*?)#gi;
# kommer att fungera för , eller ens

Tillägg: En annan användbar modifierare är /o. Det betyder "kompilera det reguljära uttrycket endast en gång". V några I vissa fall kan denna modifierare påskynda skriptet avsevärt. Jag är dock inte säker på om det stöds någon annanstans än Perl. Tack för tipset kompis

Modifierare

Minustecknet (-) som föregås av en modifierare (med undantag för U) skapar dess negation.

Speciella karaktärer

AnalogBeskrivning
() undermönster, kapslade uttryck
jokertecken
(a,b) antal förekomster från "a" till "b"
| logiskt "eller", i fallet med alternativ med ett tecken, använd
\ specialtecken försvinner
. alla tecken utom nyrad
\d decimalsiffra
\D[^\d]något annat tecken än en decimalsiffra
\f slutet (avbrott) på sidan
\n linjeöversättning
\pL UTF-8-kodad bokstav när du använder modifieraren u
\r vagnretur
\s[\t\v\r\n\f]mellanslagstecken
\S[^\s]vilken karaktär som helst utom promel
\t tabulering
\w valfri siffra, bokstav eller understreck
\W[^\w]något annat tecken än en siffra, bokstav eller understreck
\v vertikal flik

Specialtecken i en karaktärsklass

Placera i en sträng

ExempelÖverensstämmelseBeskrivning
^ ^aa aa aaabörjan av raden
$ en $aaa aa a slutet av raden
\A\Aaa aa aaa
aaa aaa
början av text
\za\zaaa aaa
aaa aa a
slutet av texten
\ba\b
\ba
aa a aa a
a aa a aa
ordgräns, påstående: det föregående tecknet är ett ord, men nästa är det inte, eller vice versa
\B\Ba\Ba a a a a aingen ordgräns
\G\Gaaaa aaaTidigare lyckad sökning, sökningen stannade vid position 4 - där a inte hittades
Ladda ner i PDF, PNG.

Ankare

Ankare i reguljära uttryck indikerar början eller slutet av något. Till exempel rader eller ord. De representeras av vissa symboler. Till exempel skulle ett mönster som matchar en sträng som börjar med ett nummer se ut så här:

Här anger tecknet ^ början på en rad. Utan det skulle mönstret matcha vilken sträng som helst som innehåller en siffra.

Karaktärsklasser

Teckenklasser i reguljära uttryck matchar på en gång en viss uppsättning tecken. Till exempel matchar \d valfri siffra från 0 till 9, \w matchar bokstäver och siffror och \W matchar alla tecken utom bokstäver och siffror. Mönstret som identifierar bokstäver, siffror och mellanslag ser ut så här:

POSIX

POSIX är ett relativt nytt tillskott till familjen med reguljära uttryck. Tanken, som med teckenklasser, är att använda förkortningar som representerar någon grupp av tecken.

Uttalanden

Till en början har nästan alla svårt att förstå påståendena, men när du blir mer bekant med dem kommer du att använda dem ganska ofta. Påståenden ger ett sätt att säga "Jag vill hitta varje ord i det här dokumentet som innehåller bokstaven 'q' som inte följs av 'werty'".

[^\s]*q(?!werty)[^\s]*

Ovanstående kod börjar med att leta efter andra tecken än ett mellanslag ([^\s]*) följt av q . Parsern når sedan "framåtblickande"-satsen. Detta gör automatiskt det föregående elementet (tecken, grupp eller teckenklass) villkorligt – det matchar bara mönstret om påståendet är sant. I vårt fall är påståendet negativt (?!), d.v.s. det kommer att stämma om det den letar efter inte hittas.

Så parsern kontrollerar de kommande tecknen enligt det föreslagna mönstret (werty). Om de hittas är påståendet falskt, vilket betyder att tecknet q kommer att "ignoreras", det vill säga att det inte matchar mönstret. Om werty inte hittas, är påståendet sant, och allt är bra med q. Den fortsätter sedan att söka efter andra tecken än ett mellanslag ([^\s]*).

kvantifierare

Kvantifierare låter dig definiera en del av ett mönster som måste upprepas flera gånger i rad. Om du till exempel vill ta reda på om ett dokument innehåller en sträng med 10 till 20 (inklusive) bokstäverna "a", kan du använda det här mönstret:

A(10,20)

Som standard är kvantifierare giriga. Därför kommer kvantifieraren +, som betyder "en eller flera gånger", att matcha det maximalt möjliga värdet. Ibland orsakar detta problem, och då kan du säga till kvantifieraren att sluta vara girig (bli "lat") med en speciell modifierare. Titta på denna kod:

".*"

Det här mönstret matchar texten som är innesluten i dubbla citattecken. Din ursprungliga rad kan dock vara ungefär så här:

Hej världen

Mallen ovan hittar följande delsträng i denna sträng:

"helloworld.htm" title="(!LANG:Hello World" !}

Han var för girig och fångade det största stycke text han kunde.

".*?"

Det här mönstret matchar även alla tecken som omges av dubbla citattecken. Men den lata versionen (märker du modifieraren?) letar efter minsta möjliga förekomst och hittar därför varje delsträng med dubbla citattecken individuellt:

"helloworld.htm" "Hello World"

Escape i reguljära uttryck

Reguljära uttryck använder vissa tecken för att representera olika delar av ett mönster. Det finns dock ett problem om du behöver hitta ett av dessa tecken i en sträng som ett vanligt tecken. En prick, till exempel, i ett reguljärt uttryck betyder "vilket tecken som helst utom en radbrytning". Om du behöver hitta en punkt i en sträng kan du inte bara använda " . » som jokertecken - detta kommer att hitta nästan allt. Så du måste berätta för analysatorn att denna punkt ska behandlas som en vanlig punkt och inte som "vilket tecken som helst". Detta görs med en flyktkaraktär.

Ett escape-tecken som föregår ett tecken, t.ex. en punkt, gör att parsern ignorerar dess funktion och behandlar den som ett normalt tecken. Det finns flera tecken som kräver sådan escape i de flesta mallar och språk. Du hittar dem i det nedre högra hörnet av fuskbladet ("Metasymboler").

Mallen för att hitta en punkt är:

\.

Andra specialtecken i reguljära uttryck matchar ovanliga element i text. Radbrytningar och tabbar, till exempel, kan skrivas från tangentbordet, men kommer sannolikt att förvirra programmeringsspråk. Escape-tecknet används här för att berätta för analysatorn att behandla nästa tecken som ett specialtecken och inte som en vanlig bokstav eller siffra.

Escape-tecken i reguljära uttryck

Strängbyte

Strängsubstitution beskrivs i detalj i nästa avsnitt "Grupper och intervall", men förekomsten av "passiva" grupper bör nämnas här. Det är dessa grupper som ignoreras vid substitution, vilket är mycket användbart om du vill använda "eller"-villkoret i mallen, men inte vill att denna grupp ska ta del av substitutionen.

Grupper och intervall

Grupper och intervall är väldigt, väldigt användbara. Det är nog lättare att börja med intervall. De låter dig specificera en uppsättning lämpliga tecken. För att till exempel kontrollera om en sträng innehåller hexadecimala siffror (0 till 9 och A till F), använd följande intervall:

För att testa motsatsen, använd ett negativt intervall, som i vårt fall matchar alla tecken utom siffror från 0 till 9 och bokstäver från A till F:

[^A-Fa-f0-9]

Grupper används oftast när ett "eller"-villkor behövs i en mall; när du behöver hänvisa till en del av en mall från en annan del av den; samt när du byter ut strängar.

Att använda "eller" är väldigt enkelt: följande mönster letar efter "ab" eller "bc":

Om du behöver hänvisa till någon av de föregående grupperna i det reguljära uttrycket ska du använda \n , där istället för n, ersätter numret önskad grupp. Du kanske vill ha ett mönster som matchar bokstäverna "aaa" eller "bbb" följt av en siffra och sedan samma tre bokstäver. Detta mönster implementeras med hjälp av grupper:

(aaa|bbb)+\1

Den första delen av mönstret söker efter "aaa" eller "bbb", och kombinerar de hittade bokstäverna till en grupp. Detta följs av en sökning efter en eller flera siffror (+), och slutligen \1 . Den sista delen av mallen hänvisar till den första gruppen och letar efter densamma. Den letar efter en matchning med texten som redan hittats av den första delen av mönstret, snarare än att matcha den. Så "aaa123bbb" kommer inte att matcha mönstret ovan, eftersom \1 kommer att leta efter "aaa" efter siffran.

Ett av de mest användbara verktyg i reguljära uttryck är strängsubstitution. När du ersätter text kan du referera till den hittade gruppen med $n . Låt oss säga att du vill göra alla ord "önskemål" fetstilt i din text. För att göra detta bör du använda funktionen för det reguljära uttrycket ersätt, som kan se ut så här:

Ersätt (mönster, ersättning, ämne)

Den första parametern kommer att vara ungefär så här (du kan behöva några extra tecken för just den här funktionen):

([^A-Za-z0-9])(önskemål)([^A-Za-z0-9])

Den kommer att hitta alla förekomster av ordet "önskemål" tillsammans med föregående och nästa tecken, såvida de inte är bokstäver eller siffror. Då kan din ersättning vara så här:

$1$2$3

Det kommer att ersätta hela strängen som hittas av mönstret. Vi startar ersättningen vid det första tecknet som hittas (det är inte en bokstav eller en siffra), och markerar det med $1 . Utan den skulle vi helt enkelt ta bort denna karaktär från texten. Detsamma gäller slutet av bytet ($3). I mitten la vi till HTML-tagg för fetstil (naturligtvis kan du använda CSS istället eller ), och markerar den andra gruppen som mönstret hittar ($2).







2022 gtavrl.ru.