regex med alle komponenter valgfrie, hvordan unngå tomme treff

 C Programming >> C C# Program >  >> C++
regex med alle komponenter valgfrie, hvordan unngå tomme treff


Jeg må behandle en kommaseparert streng som inneholder trillinger av verdier og oversette dem til kjøretidstyper, inndataene ser slik ut:


"1x2y3z,80r160g255b,48h30m50s,1x3z,255b,1h,..."

Så hver delstreng bør transformeres på denne måten:


"1x2y3z"      should become Vector3 with x = 1,  y = 2,   z = 3
"80r160g255b" should become Color with r = 80, g = 160, b = 255
"48h30m50s" should become Time with h = 48, m = 30, s = 50

Problemet jeg står overfor er at alle komponentene er valgfrie (men de bevarer rekkefølgen) så følgende strenger er også gyldige Vector3 , Color og Time verdier:


"1x3z" Vector3 x = 1, y = 0, z = 3
"255b" Color r = 0, g = 0, b = 255
"1h" Time h = 1, m = 0, s = 0

Hva har jeg prøvd så langt?


Alle komponenter valgfrie


((?:\d+A)?(?:\d+B)?(?:\d+C)?)

A , B og C erstattes med riktig bokstav for hvert tilfelle, fungerer uttrykket nesten bra, men det gir dobbelt så mange resultater (ett treff for strengen og et annet treff for en tom streng like etter første treff), for eksempel:


"1h1m1s" two matches [1]: "1h1m1s" [2]: ""
"11x50z" two matches [1]: "11x50z" [2]: ""
"11111h" two matches [1]: "11111h" [2]: ""

Dette er ikke uventet... tross alt samsvarer en tom streng med uttrykket når ALLE komponentene er tomme; så for å fikse dette problemet har jeg prøvd følgende:


1 til 3 kvantifiserer


((?:\d+[ABC]){1,3})

Men nå samsvarer uttrykket med strenger med feil rekkefølge eller til og med gjentatte komponenter!:


"1s1m1h" one match, should not match at all! (wrong order)
"11z50z" one match, should not match at all! (repeated components)
"1r1r1b" one match, should not match at all! (repeated components)

Når det gjelder mitt siste forsøk, har jeg prøvd denne varianten av mitt første uttrykk:


Match fra start ^ til slutten $


^((?:\d+A)?(?:\d+B)?(?:\d+C)?)$

Og det fungerer bedre enn den første versjonen, men det samsvarer fortsatt med den tomme strengen pluss at jeg først skal tokenisere inngangen og deretter sende hvert token til uttrykket for å sikre at teststrengen kan matche begynnelsen (^ ) og slutt ($ ) operatører.


EDIT:Lookahead-forsøk (takket være Casimir et Hippolyte)


Etter å ha lest og (prøvet å) forstå konseptet med regex lookahead og ved hjelp av svaret fra Casimir et Hippolyte har jeg prøvd det foreslåtte uttrykket:


\b(?=[^,])(?=.)((?:\d+A)?(?:\d+B)?(?:\d+C)?)\b

Mot følgende teststreng:


"48h30m50s,1h,1h1m1s,11111h,1s1m1h,1h1h1h,1s,1m,1443s,adfank,12322134445688,48h"

Og resultatene var fantastiske! den er i stand til å oppdage fullstendige gyldige treff feilfritt (andre uttrykk ga meg 3 treff på "1s1m1h" eller "1h1h1h" som ikke var ment å bli matchet i det hele tatt). Dessverre fanger den opp tomme treff hver gang en ugyldig treff blir funnet, så en "" oppdages like før "1s1m1h" , "1h1h1h" , "adfank" og "12322134445688" , så jeg endret Lookahead-betingelsen for å få uttrykket nedenfor:


\b(?=(?:\d+[ABC]){1,3})(?=.)((?:\d+A)?(?:\d+B)?(?:\d+C)?)\b

Den blir kvitt de tomme treffene i en streng som ikke samsvarer med (?:\d+[ABC]){1,3}) så de tomme samsvarer like før "adfank" og "12322134445688" er borte, men de rett før "1s1m1h" , "1h1h1h" er fortsatt oppdaget.



Så spørsmålet er:Finnes det et regulært uttrykk som samsvarer med tre triplettverdier i en gitt rekkefølge der alle komponentene er valgfrie, men bør være sammensatt av minst én komponent og ikke samsvarer med tomme strenger?


Regex-verktøyet jeg bruker er C++11.


Svar:


Ja, du kan legge til et blikk i begynnelsen for å sikre at det er minst ett tegn:


^(?=.)((?:\d+A)?(?:\d+B)?(?:\d+C)?)$

Hvis du trenger å finne denne typen understreng i en større streng (så uten å tokenisere før), kan du fjerne ankrene og bruke et mer eksplisitt undermønster i et blikk:


(?=\d+[ABC])((?:\d+A)?(?:\d+B)?(?:\d+C)?)

I dette tilfellet, for å unngå falsk positiv (siden du ser etter veldig små strenger som kan være en del av noe annet), kan du legge til ordgrenser til mønsteret:


\b(?=\d+[ABC])((?:\d+A)?(?:\d+B)?(?:\d+C)?)\b

Merk:i en kommadelt streng:(?=\d+[ABC]) kan erstattes av (?=[^,])


Noen kodesvar


"1x2y3z,80r160g255b,48h30m50s,1x3z,255b,1h,..."
"1x2y3z"
should become Vector3 with x = 1, y = 2, z = 3 "80r160g255b"
should become Color with r = 80, g = 160, b = 255 "48h30m50s"
should become Time with h = 48, m = 30, s = 50
"1x3z"
Vector3 x = 1, y = 0, z = 3 "255b"
Color r = 0, g = 0, b = 255 "1h"
Time h = 1, m = 0, s = 0
((?:\d+A)?(?:\d+B)?(?:\d+C)?) 
"1h1m1s"
two matches [1]: "1h1m1s"
[2]: ""
"11x50z"
two matches [1]: "11x50z"
[2]: ""
"11111h"
two matches [1]: "11111h"
[2]: ""
((?:\d+[ABC]){1,3}) 
"1s1m1h"
one match, should not match at all! (wrong order) "11z50z"
one match, should not match at all! (repeated components) "1r1r1b"
one match, should not match at all! (repeated components)
^((?:\d+A)?(?:\d+B)?(?:\d+C)?)$ 
\b(?=[^,])(?=.)((?:\d+A)?(?:\d+B)?(?:\d+C)?)\b 
"48h30m50s,1h,1h1m1s,11111h,1s1m1h,1h1h1h,1s,1m,1443s,adfank,12322134445688,48h"
\b(?=(?:\d+[ABC]){1,3})(?=.)((?:\d+A)?(?:\d+B)?(?:\d+C)?)\b 
^(?=.)((?:\d+A)?(?:\d+B)?(?:\d+C)?)$ 
(?=\d+[ABC])((?:\d+A)?(?:\d+B)?(?:\d+C)?) 
\b(?=\d+[ABC])((?:\d+A)?(?:\d+B)?(?:\d+C)?)\b 
#include <regex>
#include <iostream>
const std::regex r(R"~((?:^|,)((?:\d+[xrh])?(?:\d+[ygm])?(?:\d+[zbs])?))~");
int main() {
std::string test = "1x2y3z,80r160g255b,48h30m50s,1x3z,255b";
std::sregex_iterator iter(test.begin(), test.end(), r);
std::sregex_iterator end_iter;
for(;
iter != end_iter;
++iter)
std::cout <<
iter->str(1) <<
'\n';
}
1x2y3z 80r160g255b 48h30m50s 1x3z 255b 
const std::string A = "(?:\\d+[xrh])";
const std::string B = "(?:\\d+[ygm])";
const std::string C = "(?:\\d+[zbs])";
const std::regex r("(?:^|,)(" + A + B + C + "|" + A + B + "|" + A + C + "|" + B + C + "|" + A + "|" + B + "|" + C + ")");