Matching should begin with matching any number of lines XX- :
^([A-Za-z0-9]{2}-)*
Depending on the regexp mechanism you use, you may use some more compressed [[:alnum:]] . Note that [\w\d] , as originally published, is not suitable for several reasons; see Alan Moore's comment for details.
Getting the last bit is surprisingly difficult because you need to nest conditional elements. I.E. the final hyphen matches only if the previous X matches and that X matches only the first.
Please note that this approach assumes that you do not limit the number of XX- segments. In particular, please note that will correspond to XX-XX-XX-XX-XX- . You can pretty easily limit the number of XX- segments, but so that it does not correspond to a hyphen after the fifth XX bit more complicated.
In any case, back to the explanation. The next X is fine:
^([A-Za-z0-9]{2}-)*([A-Za-z0-9])?
It is also good if it is followed by another X :
^([A-Za-z0-9]{2}-)*([A-Za-z0-9]([A-Za-z0-9])?)?
And the final one - also good (assuming that it is preceded by XX ):
^([A-Za-z0-9]{2}-)*([A-Za-z0-9]([A-Za-z0-9]-?)?)?
Finally, add $ to indicate that it should occupy the entire line:
^([A-Za-z0-9]{2}-)*([A-Za-z0-9]([A-Za-z0-9]-?)?)?$
I have forked SeanA jsfiddle. Thanks, Sean!
Update
Thanks to Alan Moore, the excellent work of โlooking at the watchmenโ (see comments), I realized that you can do this quite simply with
/^([A-Za-z0-9]{2}-)*[A-Za-z0-9]{0,2}$/
Updated script for this RE .
Here you say that at the end of a series of segments XX- can be up to two X This works because if there is a hyphen at the end, it will simply become part of the optional XX- segment.
I left the above information because it solves a more general problem. For example, if each of the segments consisted of a letter and a number, you would have to take this approach.
If you want it to match XX-XX-XX-XX-XX , but not XX-XX-XX-XX-XX- , you can use
/^([A-Za-z0-9]{2}-){0,4}[A-Za-z0-9]{0,2}$/
Branched violin for this use case .