RegEx for samsvarende datomønster i Java

1. Introduksjon

Regulære uttrykk er et kraftig verktøy for å matche forskjellige typer mønstre når de brukes riktig.

I denne artikkelen vil vi bruke java.util.regex pakke for å avgjøre om en gitt String inneholder en gyldig dato eller ikke.

For en introduksjon til regulære uttrykk, se vår guide til Java Regular Expressions API.

2. Datoformatoversikt

Vi skal definere en gyldig dato i forhold til den internasjonale gregorianske kalenderen. Vårt format vil følge det generelle mønsteret: ÅÅÅÅ-MM-DD.

La oss også ta med begrepet a hoppe år som er et år som inneholder en dag 29. februar. I henhold til den gregorianske kalenderen vil vi ringe et år hoppe hvis årstallet kan deles jevnt med 4 bortsett fra de som kan deles av 100 men inkludert de som kan deles av 400.

I alle andre tilfeller, vi ringer et år regelmessig.

Eksempler på gyldige datoer:

  • 2017-12-31
  • 2020-02-29
  • 2400-02-29

Eksempler på ugyldige datoer:

  • 2017/12/31: feil tokenavgrenser
  • 2018-1-1: mangler ledende nuller
  • 2018-04-31: feil dager teller for april
  • 2100-02-29: i år er ikke sprang ettersom verdien deler seg med 100, så februar er begrenset til 28 dager

3. Implementere en løsning

Siden vi skal matche en dato med vanlig uttrykk, la oss først skissere et grensesnitt DateMatcher, som gir en singel fyrstikker metode:

offentlig grensesnitt DateMatcher {boolske kamper (strengdato); }

Vi kommer til å presentere implementeringen trinnvis nedenfor, og bygge videre mot en komplett løsning på slutten.

3.1. Matcher bredformatet

Vi begynner med å lage en veldig enkel prototype som håndterer formatbegrensningene til matcheren vår:

klasse FormattedDateMatcher implementerer DateMatcher {privat statisk mønster DATE_PATTERN = Pattern.compile ("^ \ d {4} - \ d {2} - \ d {2} $"); @Override offentlige boolske kamper (strengdato) {return DATE_PATTERN.matcher (date) .matches (); }}

Her spesifiserer vi det en gyldig dato må bestå av tre grupper av heltall atskilt med en bindestrek. Den første gruppen består av fire heltall, mens de resterende to gruppene har to heltall hver.

Matchende datoer: 2017-12-31, 2018-01-31, 0000-00-00, 1029-99-72

Ikke-samsvarende datoer: 2018-01, 2018-01-XX, 2020/02/29

3.2. Matcher det spesifikke datoformatet

Vårt andre eksempel godtar datoperioder i tillegg til formateringsbegrensningen. For enkelhets skyld har vi begrenset interessen vår til årene 1900 - 2999.

Nå som vi vellykket matchet vårt generelle datoformat, må vi begrense det ytterligere - for å sikre at datoene faktisk er riktige:

^((19|2[0-9])[0-9]{2})-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$

Her har vi introdusert tre grupper av heltalsområder som må samsvare med:

  • (19|2[0-9])[0-9]{2} dekker et begrenset antall år ved å matche et tall som starter med 19 eller 2X etterfulgt av et par sifre.
  • 0[1-9]|1[012] samsvarer med et månedsnummer i en rekkevidde på 01-12
  • 0[1-9]|[12][0-9]|3[01] samsvarer med et dagsnummer i en rekkevidde på 01-31

Matchende datoer: 1900-01-01, 2205-02-31, 2999-12-31

Ikke-samsvarende datoer: 1899-12-31, 2018-05-35, 2018-13-05, 3000-01-01, 2018-01-XX

3.3. Matcher 29. februar

For å kunne matche skuddår riktig må vi først identifisere når vi har møtt et skuddår, og sørg for at vi godtar 29. februar som en gyldig dato for disse årene.

Ettersom antall skuddår i vårt begrensede område er stort nok, bør vi bruke de riktige delingsreglene for å filtrere dem:

  • Hvis tallet dannet av de to siste sifrene i et tall er delbart med 4, er det opprinnelige tallet delbart med 4
  • Hvis de to siste sifrene i tallet er 00, kan tallet deles med 100

Her er en løsning:

^((2000|2400|2800|(19|2[0-9](0[48]|[2468][048]|[13579][26])))-02-29)$

Mønsteret består av følgende deler:

  • 2000|2400|2800 samsvarer med et sett skuddår med en skillelinje på 400 i et begrenset område på 1900-2999
  • 19|2[0-9](0[48]|[2468][048]|[13579][26])) samsvarer med alle hvitliste kombinasjoner av år som har en skillelinje på 4 og ikke har en skillelinje av 100
  • -02-29 fyrstikker 2. februar

Matchende datoer: 2020-02-29, 2024-02-29, 2400-02-29

Ikke-samsvarende datoer: 2019-02-29, 2100-02-29, 3200-02-29, 2020/02/29

3.4. Matchende generelle dager i februar

I tillegg til å matche 29. februar i løpet av skuddår, vi må også matche alle andre dager i februar (1 - 28) i alle år:

^(((19|2[0-9])[0-9]{2})-02-(0[1-9]|1[0-9]|2[0-8]))$

Matchende datoer: 2018-02-01, 2019-02-13, 2020-02-25

Ikke-samsvarende datoer: 2000-02-30, 2400-02-62, 2018/02/28

3.5. Matchende 31-dagers måneder

Månedene januar, mars, mai, juli, august, oktober og desember skal stemme mellom 1 og 31 dager:

^(((19|2[0-9])[0-9]{2})-(0[13578]|10|12)-(0[1-9]|[12][0-9]|3[01]))$

Matchende datoer: 2018-01-31, 2021-07-31, 2022-08-31

Ikke-samsvarende datoer: 2018-01-32, 2019-03-64, 2018/01/31

3.6. Matchende 30-dagers måneder

Månedene april, juni, september og november skal stemme mellom 1 og 30 dager:

^(((19|2[0-9])[0-9]{2})-(0[469]|11)-(0[1-9]|[12][0-9]|30))$

Matchende datoer: 2018-04-30, 2019-06-30, 2020-09-30

Ikke-samsvarende datoer: 2018-04-31, 2019-06-31, 2018/04/30

3.7. Gregoriansk Date Matcher

Nå kan vi kombinere alle mønstrene ovenfor i en enkelt matcher for å få et komplett GregorianDateMatcher tilfredsstiller alle begrensningene:

klasse GregorianDateMatcher implementerer DateMatcher {privat statisk mønster DATE_PATTERN = Pattern.compile ("^ ((2000 | 2400 | 2800 | (19 | 2 [0-9] (0 [48] | [2468] [048] | [13579] [ 26]))) - 02-29) $ "+" | ^ (((19 | 2 [0-9]) [0-9] {2}) - 02- (0 [1-9] | 1 [ 0-9] | 2 [0-8])) $ "+" | ^ (((19 | 2 [0-9]) [0-9] {2}) - (0 [13578] | 10 | 12 ) - (0 [1-9] | [12] [0-9] | 3 [01])) $ "+" | ^ (((19 | 2 [0-9]) [0-9] {2 }) - (0 [469] | 11) - (0 [1-9] | [12] [0-9] | 30)) $ "); @Override offentlige boolske kamper (strengdato) {return DATE_PATTERN.matcher (date) .matches (); }}

Vi har brukt en veksling karakter “|” for å matche minst en av de fire grenene. Den gyldige datoen for februar samsvarer dermed med den første grenen 29. februar i et skuddår, enten den andre grenen på en hvilken som helst dag fra 1 til 28. Datoene for de resterende månedene samsvarer med tredje og fjerde gren.

Siden vi ikke har optimalisert dette mønsteret til fordel for bedre lesbarhet, kan du gjerne eksperimentere med en lengde på det.

For øyeblikket har vi oppfylt alle begrensningene, vi introduserte i begynnelsen.

3.8. Merknad om ytelse

Analyse av komplekse regulære uttrykk kan påvirke ytelsen til utførelsesflyten betydelig. Hovedformålet med denne artikkelen var ikke å lære en effektiv måte å teste en streng for medlemskap i et sett med alle mulige datoer.

Vurder å bruke LocalDate.parse () levert av Java8 hvis det er behov for en pålitelig og rask tilnærming til å validere en dato.

4. Konklusjon

I denne artikkelen har vi lært hvordan vi kan bruke vanlige uttrykk for å matche den strengt formaterte datoen for den gregorianske kalenderen ved å gi regler for formatet, rekkevidden og lengden på måneder også.

All koden presentert i denne artikkelen er tilgjengelig på Github. Dette er et Maven-basert prosjekt, så det skal være enkelt å importere og kjøre som det er.


$config[zx-auto] not found$config[zx-overlay] not found