# Best Regular Expression for a one or two values optionally divided by a certain character

I'm looking for the most concise regex that matches one or two 4-digit years in any the following setups:

• year
• year-
• -year
• year-year

I can't think of anything slicker than this:

[\\-]?\d{4}|\d{4}\[\\-](\d{4})?

• Do you really need the "\" before the "-"?
– asoundmove
Nov 4 '11 at 1:49
• @asoundmove: In a character set, yes. But the character set itself is redundant (as it contains only a single character).
– delnan
Nov 4 '11 at 2:08
• @codesparkle: won't adding the double slash mean that we could match: \2005 ... that's not really ideal. We should only match -2005 Sep 12 '13 at 16:03
• By our current requirements, this question would be off-topic for Code Review, since it lacks a programming language tag and is therefore hypothetical code. (This question predates the existence of the rule.) Oct 1 '14 at 19:21

I am assuming you prefer the longest match. That is, if the input line is:

xyzzy 2000-2010 xyzzy


then matching 2000 or 2010 or 2000- or -2010 is not what you want, even though these would be valid matches the way you have stated the problem.

In Perl 5.10 and later, you can reduce the pattern to 20 characters:

(\d{4})-(?1)?|-?(?1)


Let's break this down.

(\d{4}) # match a year and capture the pattern
-       # match a hyphen
(?1)?   # match a year again if possible
|     #   OR,
-?      # match an initial hyphen if possible
(?1)    # match a year


Things get more complicated if you prefer to match two years even in cases such as:

xyzzy -2000-2010 xyzzy


This is a somewhat difficult one because regular expressions inherently lack memory, so you can't tell on the back whether the front existed, so I don't think one can get better than the one you wrote for that particular set. If you wanted to allow some sort of variant, you could potentially find a better one.

• You don't need the full power of memory. You can also determine whether there is any year first, via lookaround. And if that part matches, you proceed with the easy (but by itself insufficient) shortcut of making both years optional. For example (?=.*\d)\d{4}?-\d{4}? or even (abusing the fact the following pattern has only one false positive, a single dash) (?=..)\d{4}-\d{4}.
– delnan
Nov 4 '11 at 1:07
• @delnan I don't believe theoretical regexes have lookahead/behind (possibly behind), and/because that does require additional memory (to keep the original expression while analyzing the pre-condition)[If I'm wrong about that, please provide a citation]. If he were doing this in a language which allows such a construct, yes, that should work.
– Kevin
Nov 4 '11 at 1:20
• I don't think it requires additional memory, at least not if one looks ahead by a fixed number of character. You can try the lookaround, then adjust your string position pointer to be back where you started the lookaround, or you simply don't consume characters (use a seperate counter). I don't know a nice way to encode that in a DFA or NFA though, maybe that's why it's not part of regular expression theory. Either way, it's certainly available in pretty much every language that has a serious regex implementation.
– delnan
Nov 4 '11 at 1:27
• @delnan: in the question you missed the requirement that you could have a year without a hyphen.
– asoundmove
Nov 4 '11 at 1:48

If you first removed all "-" characters you could make it

(\d{4}){1,2}


or

(\d{4}|\d{8})

• If it doesn't have to work, I can make the regex zero characters long ;)
– delnan
Nov 4 '11 at 1:33
• and if we didn't have computer's we wouldn't have a job. So it all works out. Jan 25 '12 at 6:18