21. juli 2006 - 20:25Der er
35 kommentarer og 2 løsninger
Regexp der ikke tillader samme bogstav mere end én gang
Jeg har prøvet at lave et regexp, der tjekker en liste med strenge, adskilt af linjeskift, og hvis strengen udelukkende består af bogstaver forskellige fra hinanden, skal det være en match, ellers ikke. Men det er ikke lykkedes mig, så jeg håbede at få lidt hjælp herinde.
Det vil f.eks. sige at data ikke skal matche, men at dato skal matche. Der befinder sig kun bogstaver i strengene, så det er alt jeg skal tjekke på.
Nej, for jeg bruger EditPadPro, så jeg skal have markeret alle de strenge, der ikke indeholder dubletter. Så på en eller anden måde skal jeg have "negated" mit udtryk, men det er netop der jeg har mit problem :-S
Yep darkstar, odense ville også være en dublet. Og det med små og store bogstaver har ingen betydning, som nævnt bruger jeg EditPad Pro, som standard skelner den ikke mellem store og små bogstaver, uanset om jeg bruger [A-Za-z] eller [A-Z] :)
nielle, samme resultat :( Hvilket program bruger du til at tjekke det med?
Jeg tjekker i C# men ifølge Reg Edit Pro forfatterens egne ord så er REP fuldstændig kompatibel med den regex variant som bruges af .Net.
Og når han selv siger det så er jeg tilbøjelig til at tro på det, for det er *skisme* en man som ved noget om regex'es! Det er f.eks. ham som står bag http://www.regularexpressions.info og RegBuddy.
Det er netop også den side, jeg har kigget på, og derigennem jeg fandt EditPad Pro. Og jeg kan da se at at delen om regexp i hjælp filen er magen til den på sitet, så jeg formodede da at han også var manden bag REP.
Men så forstår jeg overhovedet ikke hvor det går galt, blot at alle linjer, inklusive den linje med et enkelt ciffer, men ekslusive de linjer der består kun af cifre, men mere end 1, alle bliver highlightet.
Hvilket i REP blot giver at de selv samme linjer fra før får deres første bogstav highlightet og intet andet :( Jeg tror jeg bliver nødt til at kigge den tutorial grundigt igennem, for jeg SKAL have det til at virke i REP, at det virker i C# eller andet hjælper mig overhovedet ikke :(
Hvis odense også tæller som en dublet-streng, tvivler jeg meget kraftigt på at det kan lade sig gøre. Nielles regex ser ikke ud til at være standard. Hvad betyder \1? Kan du henvise til noget dokumentation?
\1 er det, der andetsteds hedder $1, altså en reference til den karakter den er nået til ([a-z]). Standard eller ej, bare det virker i REP er jeg glad :D
Jeg opdagede lige at der manglede et $ i dit seneste regexp, nielle. Med den tilføjet er resultatet desværre som før, alle linjer pånær cifrene bliver highlightet :(
21/07-2006 23:47:17> Hvorfor SKAL det gøres i EditPad Pro? Bare af ren nysgerrighed.
I mellemtiden har jeg så installeret EPP, og jeg må tilstå at jeg heller ikke kan få det til at fungere. Nu er jeg efterhånden parat til at smide håndklædet i ringen. Jeg ved ikke om det hjælper at læse manualen endnu engang (ellers kig efter "negativ lookaround"), for det er netop det jeg har forsøgt at gøre (godt nok i hans bog i stedet for EPP's manual).
22/07-2006 00:05:35> Nu er der faktisk ikke noget som hedder standard inden for regex'es. Hvert programmeringssprog (og editor) har sin egen smagsvariant af hvordan de er implementeret. Når det så er sagt så er \1 altså en af de ting som er med i stort set hver eneste regex-variant.
Desuden bruger min regex negativ lookahead, og en regex-motor som understøtter dette har ihvertfald også \1.
22/07-2006 00:21:33> Nej, $1 og \1 er nu ikke helt det samme. \1 bruges til backtracking, og $1 bruges i forbindelse med at man har brug for at udtrække værdien af en grouping, f.eks. i forbindelse med en replace.
22/07-2006 00:28:07> Beklager den med det manglende $ - det var en dum cut'n'paste fejl.
22/07-2006 01:10:17> Så vidt jeg husker er [:alpha:] bare en anden måde at skrive a-z på? Jeg må indrømme at jeg aldrig selv har set nogen større grund til at bruge den (eller dens søskene).
darkstar, jeg ændrede :alpha: til a-z og fjernede den en [] i hvert udtryk, og så virkede det tilsyneladende (jeg har ikke tænkt mig at løbe hvert eneste ord igennem for at sikre mig at det virker 100 % :-P). Og jeg prøvede at minimere det lidt, og kunne i hvert fald fjerne den 4. alpha plus *, så mit udtryk blev således.
^(?![a-z]*([a-z])[a-z]*\1)[a-z]*$
Men kan du forklare mig hvordan det fungerer? Jeg forstår ikke hvorfor der skal være så mange [a-z]* i det regex.
nielle, jeg laver det i EPP (ikke REP, my bad) grundet dette indlæg:
Jeg blev anbefalet at bruge en tekst editor i stedet for, og faldt over EPP, som jo også har været ganske fortrinlig til dette :) Men jeg er da glad for at det ikke bare er fordi jeg er dum, at det var problematisk at få til at virke i EPP :-P
Mht. forskellen mellem $1 og \1, sandt nok, min fejl.
Jeg kan se at jeg vist alligevel skal kigge den tutorial igennem, regex udtryk har altid forvirret mig mere end godt er, således også i dette tilfælde.
Som du kan se af mit svar så testede jeg mit forslag (i perl) og det virkede fint. [:alpha:] er standard, men det virker åbenbart ikke i dit miljø.
Den næstsidste [[:alpha:]]* var overflødig, så det er helt korrekt at du har fjernet den.
Det hele betyder:
^ match start af strengen (?! udtrykket efter denne parantes må kun matche hvis det ikke starter med det som er indeni parantesen [[:alpha:]]* nogle bogstaver ([[:alpha:]]) et bogstav som vi husker (i \1) [[:alpha:]]* nogle bogstaver \1 det bogstav som vi huskede tidligere [[:alpha:]]* nogle flere bogstaver (dette led kan udelades) )[[:alpha:]]* nogle flere bogstaver $ strengens slutning
Princippet er det samme for din version, men bemærk at [a-z] kun matcher lige præcis a til z, hvor [:alpha:] med de rigtige indstillinger også matcher sjove bogstaver, som é, æ, ö og den slags.
Heh, du behøver ikke at undskylde for at du har skrevet REP ... det var mig som kom til at indføre den "forkortelse" tilbage i 21/07-2006 21:44:59. My bad. :^)
Løsningen fra 22/07-2006 11:36:20 kan iøvrigt forkortes til:
^(?!.*([a-z]).*\1)[a-z]*$
- eller endda til:
^(?!.*(.).*\1)[a-z]*$
(og den dur faktisk også i C# ;^)
Jeg skal gerne forsøge at forklare den:
^ og $ - er ankre til hhv. start og slut af strengen. De sikre at matchningen sker på hele linjer.
(?! ... ) - er "negativ lookahead". Den matcher noget som ikke er efterfulgt af det der står på "..." pladsen.
Når der altså står:
^(?! ... ) - så matcher den starten af linjen blot denne ikke er efterfulgt af "...".
Det der står på "..." pladsen:
.*(.).*\1
- eller splittet op i dens enkelte led:
.* (.) .* \1 - matcher 0 eller flere tegn, derefter ét tegn som bliver fanget af ()-gruppen, derefter 0 eller flere tegn og så til sidst noget som er lig med det som blev fanget tidligere i forløbet af ()-gruppen. Det matcher med andre ord noget som er en dublet (den dublet er i øvrigt ikke bundet til at være et bogstav).
Hvis det indsættes i vores negative lookahead:
^(?!.*(.).*\1) - matcher det starten af en linje, hvis der ikke efterfølgende er en dublet. Den allerførste ".*" i ".*(.).*\1" skal være med for at tage højde for at dubletten jo kan ligge inde i linjen, i stedet for i starten. Det er vigtigt at have den hvis vi skal bruge udtrykket sammen med vores negative lookahead og ^-ankret.
Den sidste [a-z]*:
^(?!.*(.).*\1)[a-z]*$ - er med for at fylde ud til enden af strengen. Den har dog også den vigtige funktion at den sørger for at regex'en ikke matcher med noget som helst andet end bogstaver.
Ja sorry, men jeg har ikke tjekket min mail i et stykke tid :(
nielle, smid et svar, den forklaring er jo guld værd :D Jeg forstår oven i købet nu hvorfor jeg kunne fjerne den 4. [a-z] fra det regex.
Men et lille tillægs spørgsmål. Ville den regex ikke også kunne matche en tom linje, altså bare et linjeskift pga [a-z]*, altså 0 eller flere forekomster af bogstaver? Eller er jeg helt gal på den?
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.