RegEx til matchende datomønster i Java

1. Introduktion

Regulære udtryk er et kraftfuldt værktøj til at matche forskellige slags mønstre, når de bruges korrekt.

I denne artikel bruger vi java.util.regex pakke for at afgøre, om en given Snor indeholder en gyldig dato eller ej.

For en introduktion til regulære udtryk henvises til vores guide til Java Regular Expressions API.

2. Datoformatoversigt

Vi definerer en gyldig dato i forhold til den internationale gregorianske kalender. Vores format følger det generelle mønster: ÅÅÅÅ-MM-DD.

Lad os også medtage begrebet a springe år, der er et år, der indeholder en dag den 29. februar. I henhold til den gregorianske kalender kalder vi et år springe hvis årstallet kan deles jævnt med 4 undtagen dem, der kan deles med 100 men inklusive dem, der kan deles af 400.

I alle andre tilfælde, vi ringer et år fast.

Eksempler på gyldige datoer:

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

Eksempler på ugyldige datoer:

  • 2017/12/31: forkert tokenafgrænser
  • 2018-1-1: mangler førende nuller
  • 2018-04-31: forkerte dage tæller for april
  • 2100-02-29: dette år springer ikke, da værdien dividerer med 100, så februar er begrænset til 28 dage

3. Implementering af en løsning

Da vi skal matche en dato ved hjælp af regulære udtryk, skal vi først skitsere en grænseflade DateMatcher, som giver en enkelt Tændstikker metode:

offentlig grænseflade DateMatcher {boolske matches (strengdato); }

Vi præsenterer implementeringen trin for trin nedenfor og bygger videre mod en komplet løsning i slutningen.

3.1. Matcher det brede format

Vi starter med at oprette en meget enkel prototype, der håndterer formatbegrænsningerne for vores matcher:

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

Her specificerer vi det en gyldig dato skal bestå af tre grupper af heltal adskilt af en bindestreg. Den første gruppe består af fire heltal, hvor de resterende to grupper har to heltal hver.

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

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

3.2. Matcher det specifikke datoformat

Vores andet eksempel accepterer intervaller af dato-tokens såvel som vores formateringsbegrænsning. For enkelheds skyld har vi begrænset vores interesse til årene 1900 - 2999.

Nu hvor vi med succes matchede vores generelle datoformat, skal vi begrænse det yderligere - for at sikre, at datoerne faktisk er korrekte:

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

Her har vi introduceret tre grupper af heltalsområder, der skal matche:

  • (19|2[0-9])[0-9]{2} dækker et begrænset antal år ved at matche et nummer, der starter med 19 eller 2X efterfulgt af et par af alle cifre.
  • 0[1-9]|1[012] matcher et månedstal i en række af 01-12
  • 0[1-9]|[12][0-9]|3[01] matcher et dagnummer i en række af 01-31

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

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

3.3. Matchende den 29. februar

For at kunne matche skuddår korrekt skal vi først identificere, hvornår vi er stødt på et skudår, og sørg derefter for, at vi accepterer den 29. februar som en gyldig dato for disse år.

Da antallet af skudår i vores begrænsede rækkevidde er stort nok, bør vi bruge de relevante regler for delbarhed til at filtrere dem:

  • Hvis tallet dannet af de sidste to cifre i et tal kan deles med 4, kan det oprindelige tal deles med 4
  • Hvis de sidste to cifre 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 af følgende dele:

  • 2000|2400|2800 matcher et sæt skuddår med en skillevægge på 400 i et begrænset interval på 1900-2999
  • 19|2[0-9](0[48]|[2468][048]|[13579][26])) matcher alle hvidliste kombinationer af år, som har en skillevæg på 4 og ikke har en skillerum af 100
  • -02-29 Tændstikker 2. februar

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

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

3.4. Matchende generelle dage i februar

Ud over at matche 29. februar i skudår, vi skal også matche alle andre dage 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-matchende datoer: 2000-02-30, 2400-02-62, 2018/02/28

3.5. Matchende 31-dages måneder

Månederne januar, marts, maj, juli, august, oktober og december skal svare til mellem 1 og 31 dage:

^(((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-matchende datoer: 2018-01-32, 2019-03-64, 2018/01/31

3.6. Matchende 30-dages måneder

Månederne april, juni, september og november skal matche mellem 1 og 30 dage:

^(((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-matchende datoer: 2018-04-31, 2019-06-31, 2018/04/30

3.7. Gregoriansk dato matcher

Nu kan vi kombinere alle ovenstående mønstre i en enkelt matcher for at få et komplet GregorianDateMatcher opfylder alle begrænsningerne:

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 kampe (strengdato) {returner DATE_PATTERN.matcher (dato) .matches (); }}

Vi har brugt en skiftevis karakter “|” at matche mindst en af de fire grene. Den gyldige dato for februar svarer således til den første gren af ​​29. februar i et skudår, enten den anden gren på en hvilken som helst dag fra 1 til 28. Datoerne for de resterende måneder svarer til tredje og fjerde gren.

Da vi ikke har optimeret dette mønster til fordel for en bedre læsbarhed, er du velkommen til at eksperimentere med en længde på det.

I dette øjeblik har vi opfyldt alle de begrænsninger, vi introducerede i starten.

3.8. Bemærkning om ydeevne

Analyse af komplekse regulære udtryk kan påvirke udførelsen af ​​udførelsesstrømmen markant. Det primære formål med denne artikel var ikke at lære en effektiv måde at teste en streng for dens medlemskab på et sæt af alle mulige datoer.

Overvej at bruge LocalDate.parse () leveres af Java8, hvis der er behov for en pålidelig og hurtig tilgang til validering af en dato.

4. Konklusion

I denne artikel har vi lært, hvordan man bruger regulære udtryk til at matche den strengt formaterede dato for den gregorianske kalender ved at give regler for formatet, rækkevidden og længden af ​​måneder også.

Al den kode, der præsenteres i denne artikel, er tilgængelig på Github. Dette er et Maven-baseret projekt, så det skal være let at importere og køre som det er.


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