关于javascript:正则表达式恰好匹配5个数字和一个可选空间

Regex to match exactly 5 numbers and one optional space

我最近需要创建一个正则表达式来检查javascript中的输入。输入的长度可以是5或6个字符,必须正好包含5个数字和一个可选的空格,可以是字符串中的任何位置。我一点都不懂雷吉斯,尽管我试图寻找更好的方法,但最终还是得到了这样的结果:

1
(^\d{5}$)|(^ \d{5}$)|(^\d{5} $)|(^\d{1} \d{4}$)|(^\d{2} \d{3}$)|(^\d{3} \d{2}$)|(^\d{4} \d{1}$)

这是我需要的,所以允许的输入是(如果0是任何数字)

1
2
3
4
5
6
7
'00000'  
' 00000'  
'0 0000'  
'00 000'  
'000 00'  
'0000 0'  
'00000 '

我怀疑这是实现与regex匹配的唯一方法,但我还没有找到一种更干净的方法。所以我的问题是,如何才能写得更好?

谢谢您。

编辑:所以,这是可能的!汤姆·洛德的回答满足了我对正则表达式的需求,所以我把它标为我问题的正确答案。

然而,在我发布这个问题后不久,我意识到我的想法不正确,因为项目中的其他输入很容易通过regex"验证",所以我立即假设我也可以用它验证这个输入。

结果我只能这样做:

1
2
3
4
5
const validate = function(value) {
    const v = value.replace(/\s/g, '')
    const regex = new RegExp('^\\d{5}$');
    return regex.test(v);
}

感谢大家的回答和想法!:)

edit2:我忘记提到一个可能非常重要的细节,那就是输入是有限的,所以用户最多只能输入6个字符。我很抱歉。


Note: Using a regular expression to solve this problem might not be
the best answer. As answered
below, it may be
easier to just count the digits and spaces with a simple function!

However, since the question was asking for a regex answer, and in some
scenarios you may be forced to solve this with a regex (e.g. if
you're tied down to a certain library's implementation), the following
answer may be helpful:

此regex与正好包含5位数字的行匹配:

1
^(?=(\D*\d){5}\D*$)

此regex匹配包含一个可选空格的行:

1
^(?=[^ ]* ?[^ ]*$)

如果我们把它们放在一起,并且确保字符串只包含数字和空格([\d ]*$),我们得到:

1
^(?=(\D*\d){5}\D*$)(?=[^ ]* ?[^ ]*$)[\d ]*$

您也可以在该模式的末尾使用[\d ]{5,6}而不是[\d ]*,达到同样的效果。

演示

说明:

此正则表达式正在使用lookaheads。这些是零宽度模式匹配器,这意味着模式的两个部分都"锚定"在字符串的开头。

  • \d表示"任何数字",\d表示"任何非数字"。

  • 表示"空间",[^ ]表示"任何非空间"。

  • \D*\d被重复5次,以确保字符串中正好有5位数字。

下面是一个实际中的正则表达式的可视化:

regex visualisation

请注意,如果您实际上希望"可选空间"包括选项卡之类的内容,那么您可以使用\s\s

更新:由于这个问题似乎得到了相当多的牵引力,我想澄清一些关于这个答案的问题。

对于我上面的答案,有几个"简单"的变体解决方案,例如:

1
2
3
4
5
6
7
8
// Only look for digits and spaces, not"non-digits" and"non-spaces":
^(?=( ?\d){5} *$)(?=\d* ?\d*$)

// Like above, but also simplifying the second lookahead:
^(?=( ?\d){5} *$)\d* ?\d*

// Or even splitting it into two, simpler, problems with an"or" operator:
^(?:\d{5}|(?=\d* \d*$).{6})$

上面每行的演示:1 2 3

甚至,如果我们可以假设字符串不超过6个字符,那么即使这样也足够:

1
^(?:\d{5}|\d* \d*)$

因此,考虑到这一点,您为什么要对类似的问题使用原始的解决方案呢?因为它是通用的。再看一下我的原始答案,用空格重新书写:

1
2
3
4
^
(?=(\D*\d){5}\D*$) # Must contain exactly 5 digits
(?=[^ ]* ?[^ ]*$)  # Must contain 0 or 1 spaces
[\d ]*$            # Must contain ONLY digits and spaces

这种使用连续look a heads的模式可以在各种场景中使用,以编写高度结构化且(可能令人惊讶)易于扩展的模式。

例如,假设规则已更改,您现在想要匹配2-3个空格、1个.和任意数量的连字符。实际上很容易更新regex:

1
2
3
4
5
^
(?=(\D*\d){5}\D*$)       # Must contain exactly 5 digits
(?=([^ ]* ){2,3}[^ ]*$)  # Must contain 2 or 3 spaces
(?=[^.]*\.[^.]*$)        # Must contain 1 period
[\d .-]*$   # Must contain ONLY digits, spaces, periods and hyphens

…所以总的来说,有"更简单"的regex解决方案,而且很可能是一个更好的非regex解决方案来解决OP的特定问题。但我提供的是一个通用的、可扩展的设计模式,用于匹配这种类型的模式。


我建议首先检查五个数字^\d{5}$,或者在六个字符.{6}$中的数字^(?=\d* \d*$)之间寻找一个空格。

结合这些部分表达可得到^\d{5}$|^(?=\d* \d*$).{6}$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let regex = /^\d{5}$|^(?=\d* \d*$).{6}$/;

console.log(regex.test('00000'));   // true
console.log(regex.test(' 00000'));  // true
console.log(regex.test('00000 '));  // true
console.log(regex.test('00 000'));  // true
console.log(regex.test('  00000')); // false
console.log(regex.test('00000  ')); // false
console.log(regex.test('00  000')); // false
console.log(regex.test('00 0 00')); // false
console.log(regex.test('000 000')); // false
console.log(regex.test('0000'));    // false
console.log(regex.test('000000'));  // false
console.log(regex.test('000 0'));   // false
console.log(regex.test('000 0x'));  // false
console.log(regex.test('0000x0'));  // false
console.log(regex.test('x00000'));  // false

或者通过以下方式单独匹配部分表达式:

1
/^\d{5}$/.test(input) || input.length == 6 && /^\d* \d*$/.test(input)


这对我来说更直观,是O(n)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function isInputValid(input) {
    const length = input.length;
    if (length != 5 && length != 6) {
        return false;
    }

    let spaceSeen = false;
    let digitsSeen = 0;
    for (let character of input) {
        if (character === ' ') {
            if (spaceSeen) {
                return false;
            }
            spaceSeen = true;
        }
        else if (/^\d$/.test(character)) {
            digitsSeen++;
        }
        else {
            return false;
        }
    }

    return digitsSeen == 5;
}


下面是一个简单的regex来完成这项工作:

1
^(?=[\d ]{5,6}$)\d*\s?\d*$

说明:

^断言字符串开头的位置

正面展望(?= [d] {5,6}$)

断言下面的regex匹配

匹配下面列表中的单个字符[d]5,6

5,6量词-匹配5到6次,尽可能多次,根据需要反馈(贪心)

D匹配一个数字(等于[0-9])字面匹配字符(区分大小写)

$断言字符串末尾的位置 D*匹配一个数字(等于[0-9])

  • 量词-在零和无限次之间匹配,尽可能多次,根据需要反馈(贪婪)

s匹配任何空白字符(等于[
fv])

D*匹配一个数字(等于[0-9])

  • 量词-在零和无限次之间匹配,尽可能多次,根据需要反馈(贪婪)

$断言字符串末尾的位置


你可以把它分成两半:

1
2
3
4
var input = '0000 ';

if(/^[^ ]* [^ ]*$/.test(input) && /^\d{5,6}$/.test(input.replace(/ /, '')))
  console.log('Match');


这就是我不使用regex的方式:

1
2
3
4
5
string => [...string].reduce(
    ([spaces,digits], char) =>
        [spaces += char == ' ', digits += /\d/.test(char)],
    [0,0]
).join(",") =="1,5";


1
2
3
4
string="12345";
if(string.length<=6 && string.replace(/\s/g, '').length<=5 && parseInt(string,10)){
  alert("valid");
}

你可以简单地检查长度,如果它是一个有效的数字…