Validate that string is the beginning of a regex pattern

时间:2016-02-12 19:49:57

标签: javascript regex

I can successfully validate a complete Canadian zip code (like "M1B0A1") but how can I validate that a string can be an incomplete Canadian zip (like "M1B")? I want to check that the existing content could match a regex if more characters are typed later. As soon as the user enters a string that cannot possibly be the start of a Canadian zip, I'd like to show an error.

My existing regex for validating a complete Canadian zip is:

^[ABCEGHJKLMNPRSTVXY]\d[ABCEGHJKLMNPRSTVWXYZ] ?\d[ABCEGHJKLMNPRSTVWXYZ]\d$

I tried to modify the regex by making each character optional with this regex:

^[ABCEGHJKLMNPRSTVXY]?\d?[ABCEGHJKLMNPRSTVWXYZ]? ?\d?[ABCEGHJKLMNPRSTVWXYZ]?\d?$

but then that incorrectly accepts string like "777" or "HA16". Can I do this with regex, or am I going to have to create a set of regexes of varying string length? I'd rather avoid something like

switch(str.length) {
    case 1:
        return /[ABCEGHJKLMNPRSTVXY]/.test(str);
    case 2:
        return /[ABCEGHJKLMNPRSTVXY]\d/.test(str);
    //.... and more
}

2 个答案:

答案 0 :(得分:1)

Solution 1

I suppose you could use the 'OR' | and append each of the regex patterns from the switch-case list that you posted, that way the regex would practically do the work of the switch-case for you. Edit: the order of each case should be reversed, though, so as to match the most characters possible.

Template:

^(A|B|C|D|E|F)$

The letters should be replaced with the following

A: the complete regex pattern              (Case 6)
B: regex pattern for first 5 characters    (Case 5)
C: regex pattern for first 4 characters    (Case 4)
D: regex pattern for first 3 characters    (Case 3)
E: regex pattern for first 2 characters    (Case 2)
F: regex pattern for the first character   (Case 1)

The end result isn't pretty: DEMO


Edit [16-FEB-2016]

Solution 2

To avoid typing out each of the cases, you could nest optional groups. Each group would represent the pattern of each zip code character so that when the first character is correctly matched then the next character will be optional; then if that character is matched then the next one would become optional, and so on. (This might be what you were attempting to do in your second regex pattern).

Template:

^(A(B(C(D(E(F)?)?)?)?)?)$

The letters should be replaced as follows

A: regex pattern for the 1st zip code character   
B: regex pattern for the 2nd zip code character
C: regex pattern for the 3rd zip code character
D: regex pattern for the 4th zip code character
E: regex pattern for the 5th zip code character
F: regex pattern for the 6th zip code character

Result (DEMO)

^(\[ABCEGHJKLMNPRSTVXY\](\d(\[ABCEGHJKLMNPRSTVWXYZ\]( ?(\d(\[ABCEGHJKLMNPRSTVWXYZ\](\d)?)?)?)?)?)?)$

Additionally, if you're not interested in the captured groups then you could modify the above to use non-capturing groups: DEMO. It's your call.

答案 1 :(得分:-1)

I changed your regex to "-$1"

Basically it divides the following regex ^[ABCEGHJKLMNPRSTVXY](?:(?:\d$)|(?:\d[ABCEGHJKLMNPRSTVWXYZ]$)|(?:\d[ABCEGHJKLMNPRSTVWXYZ]\d$)|(?:\d[ABCEGHJKLMNPRSTVWXYZ]\d[ABCEGHJKLMNPRSTVWXYZ]$)|(?:\d[ABCEGHJKLMNPRSTVWXYZ]\d[ABCEGHJKLMNPRSTVWXYZ]\d$))?$ into 6 parts, which is each digit, than I make the first one obrigatory and the following ones optional, making sure the order you want, that is, if one digit is skipped it will accept if that is the end of the string or fail if there is more digits after the skipped one.

I think this is what you want, here is a regex101 test.