正则表达式检查具有打包空间的修复长度字段

时间:2009-08-11 03:57:37

标签: regex

假设我有一个要解析的文本文件,其中包含一些固定长度的内容:

123jackysee        45678887
456charliewong     32145644
<3><------16------><--8---> # Not part of the data.

前三个字符是ID,然后是16个字符的用户名,然后是8位电话号码。

我想编写一个正则表达式来匹配并验证每一行的输入,我想出的那个:

(\d{3})([A-Za-z ]{16})(\d{8})

用户名应包含8-16个字符。但是([A-Za-z ]{16})也会匹配空值或空格。我想到([A-Za-z]{8,16} {0,8})但它会检测超过16个字符。有什么建议吗?

7 个答案:

答案 0 :(得分:8)

不,不,不,不! : - )

为什么人们会坚持尝试将如此多的功能打包到单个RE或SQL语句中?

我的建议,做类似的事情:

  • 确保长度为27。
  • 将三个组件提取为单独的字符串(0-2,3-18,19-26)。
  • 检查第一个匹配"\d{3}"
  • 检查第二个是否匹配"[A-Za-z]{8,} *"
  • 检查第三个是否与"\d{8}"匹配。

如果您希望整个检查符合一行源代码,请将其放在函数isValidLine()中,然后调用它。

即使是这样的事情也可以解决问题:

def isValidLine(s):
    if s.len() != 27 return false
    return s.match("^\d{3}[A-za-z]{8,} *\d{8}$"):

不要误以为这是干净的Python代码,它实际上是PaxLang,我自己的专有伪代码。希望很明显,第一行检查长度是27,第二行是否匹配给定的RE。

由于第一行,中间字段总共自动16个字符,并且其他两个字段在RE中是固定长度的。 RE还确保它有八个或更多个alpha,然后是正确的空格数。

用一个RE做这种事情就像是一些怪物:

^\d{3}(([A-za-z]{8} {8})
      |([A-za-z]{9} {7})
      |([A-za-z]{10} {6})
      |([A-za-z]{11} {5})
      |([A-za-z]{12}    )
      |([A-za-z]{13}   )
      |([A-za-z]{14}  )
      |([A-za-z]{15} )
      |([A-za-z]{16}))
      \d{8}$

您可以通过确保它传递两个单独的 RE:

来实现
^\d{3}[A-za-z]{8,} *\d{8}$
^.{27}$

但是,由于最后一个只是一个长度检查,它与上面的isValidLine()没有区别。

答案 1 :(得分:0)

假设你的意思是perl正则表达式,如果你在用户名中允许'_':

perl -ne 'exit 1 unless /(\d{3})(\w{8,16})\s+(\d{8})/ && length == 28'

答案 2 :(得分:0)

嗯......根据您正在运行的正则表达式的确切版本,请考虑:

(?P<id>\d{3})(?=[A-Za-z\s]{16}\d)(?P<username>[A-Za-z]{8,16})\s*(?P<phone>\d{8})

注意100%确定这会有效,而且我使用了空格转义字符而不是实际空格 - 我对自己的空间字符感到紧张,但你可能想要更严格。

看看它是否有效。我自己只与RegEx中介,所以我可能会出错。

查看您的RegEx版本的命名组语法a)是否存在,b)是否符合我上面使用的标准。

编辑:

为那些没有很多RegEx经验的人扩展我想要做的事(抱歉让你的眼睛流血,Pax!):

(?P<id>\d{3})

这将尝试匹配命名的捕获组 - 'id' - 长度为三位数。大多数版本的RegEx允许您使用命名捕获组来提取您匹配的值。这使您可以同时进行验证和数据捕获 。不同版本的RegEx具有稍微不同的语法 - 请查看http://www.regular-expressions.info/named.html以获取有关特定实现的更多详细信息。

(?=[A-Za-z\s]{16}\d)

?=是一个先行操作员。这将查看接下来的16个字符,如果它们都是字母或空白字符,则返回true,后面跟一个数字。前瞻操作符的长度为零,因此它实际上不会返回任何内容。您的RegEx字符串从Lookahead开始的位置继续。有关前瞻的更多详细信息,请查看http://www.regular-expressions.info/lookaround.html

(?P<username>[A-Za-z]{8,16})\s*

如果前瞻通过,那么我们继续计算第四个字符。我们想要找到八到十六个字符,然后是零个或多个空格。 “或更多”实际上是安全的,因为我们已经确定在前瞻中,在下一个数字之前总共不能超过16个字符。

最后,

(?P<phone>\d{8})

这应该检查八位数的电话号码。

我有点紧张,这不会完全奏效 - 您的RegEx版本可能不支持我习惯的命名组语法或前瞻语法。

我也有点紧张,这个正则表达式会成功匹配一个空字符串。不同版本的Regex处理空字符串的方式不同。

您可能还想考虑将此正则表达式锚定在^和$之间,以确保您与整行匹配,而不仅仅是更大行的一部分。

答案 3 :(得分:0)

我会使用你建议的正则表达式添加一小部分:

(\d{3})([A-Za-z]{3,16} {0,13})(\d{8})

将匹配具有非空白用户名但仍允许空格填充的内容。唯一的补充是你必须检查每个输入的长度以验证正确的字符数。

答案 4 :(得分:0)

@ OP,并非每个问题都需要正则表达式。你的问题很容易检查。根据您使用的语言,它们将具有某种内置字符串函数。使用它们。 以下最小的例子是在Python中完成的。

import sys
for line in open("file"):
    line=line.strip()
    # check first 3 char for digit
    if not line[0:3].isdigit(): sys.exit()
    # check length of username.
    if len(line[3:18]) <8 or len(line[3:18]) > 16: sys.exit()
    # check phone number length and whether they are digits.
    if len(line[19:26]) == 8 and not line[19:26].isdigit(): sys.exit()
    print line

答案 5 :(得分:0)

我也认为您不应该尝试将所有功能打包到单个正则表达式中。这是一种方法:

#!/usr/bin/perl

use strict;
use warnings;

while ( <DATA> ) {
    chomp;
    last unless /\S/;
    my @fields = split;
    if (
        ( my ($id, $name) = $fields[0] =~ /^([0-9]{3})([A-Za-z]{8,16})$/ )
            and ( my ($phone) = $fields[1] =~ /^([0-9]{8})$/ )
    ) {
        print "ID=$id\nNAME=$name\nPHONE=$phone\n";
    }
    else {
        warn "Invalid line: $_\n";
    }
}

__DATA__
123jackysee       45678887
456charliewong    32145644
678sdjkfhsdjhksadkjfhsdjjh 12345678

这是另一种方式:

#!/usr/bin/perl

use strict;
use warnings;

while ( <DATA> ) {
    chomp;
    last unless /\S/;
    my ($id, $name, $phone) = unpack 'A3A16A8';
    if ( is_valid_id($id)
            and is_valid_name($name)
            and is_valid_phone($phone)
    ) {
        print "ID=$id\nNAME=$name\nPHONE=$phone\n";
    }
    else {
        warn "Invalid line: $_\n";
    }
}

sub is_valid_id    { ($_[0]) = ($_[0] =~ /^([0-9]{3})$/) }

sub is_valid_name  { ($_[0]) = ($_[0] =~ /^([A-Za-z]{8,16})\s*$/) }

sub is_valid_phone { ($_[0]) = ($_[0] =~ /^([0-9]{8})$/) }

__DATA__
123jackysee        45678887
456charliewong     32145644
678sdjkfhsdjhksadkjfhsdjjh 12345678

泛化:

#!/usr/bin/perl

use strict;
use warnings;

my %validators = (
    id    => make_validator( qr/^([0-9]{3})$/ ),
    name  => make_validator( qr/^([A-Za-z]{8,16})\s*$/ ),
    phone => make_validator( qr/^([0-9]{8})$/ ),
);

INPUT:
while ( <DATA> ) {
    chomp;
    last unless /\S/;
    my %fields;
    @fields{qw(id name phone)} = unpack 'A3A16A8';

    for my $field ( keys %fields ) {
        unless ( $validators{$field}->($fields{$field}) ) {
            warn "Invalid line: $_\n";
            next INPUT;
        }
    }

    print "$_ : $fields{$_}\n" for qw(id name phone);
}

sub make_validator {
    my ($re) = @_;
    return sub { ($_[0]) = ($_[0] =~ $re) };
}

__DATA__
123jackysee        45678887
456charliewong     32145644
678sdjkfhsdjhksadkjfhsdjjh 12345678

答案 6 :(得分:0)

您可以使用前瞻:^(\d{3})((?=[a-zA-Z]{8,})([a-zA-Z ]{16}))(\d{8})$

测试:

    123jackysee        45678887      Match
    456charliewong     32145644      Match
    789jop             12345678      No Match - username too short
    999abcdefghijabcde12345678       No Match - username 'column' is less that 16 characters
    999abcdefghijabcdef12345678      Match
    999abcdefghijabcdefg12345678     No Match - username column more that 16 characters