有没有一种方法可以解析这些字符串?

4
如果有的话,我肯定没看到。我们正在读取驾照上的磁条信息。数据似乎不太一致。驾照应该遵循的标准规定了每个字段可以拥有的长度限制。让我困惑的是如何解析这些数据。
例如,一个字段可能允许总共13个字符,但只使用了8个。在这种情况下,字符串的那部分总会以插入符号作为分隔符结束。然而,这里有一个棘手的问题,如果一个字段恰好是13个字符(达到了允许的13个字符),则没有结束插入符号和右填充。所有的数据都连在一起。
以下是两个示例字符串。
%CAMISSION HILLSSMITH$JOHN$JIM$JR^1147 SOMESTREET^?
%CALOS ANGELES^DOE$JOHN$CARL^14324 MAIN ST APT 5^?

使用PHP,我该如何做到这一点?我真的很需要帮助。我真的很困惑。
4个回答

5

好的,我们开始吧。我使用了x标志来使正则表达式更易读,并能够进行注释。

根据@EboMike发布的规范,每个字段都有最大长度,如果长度小于该长度,则以^结尾。姓名是一个复合字段,使用$作为姓氏、名字、中间名和后缀之间的分隔符。地址也是一样,如果地址有多行,则使用$

$licenses = array(
    '%CAMISSION HILLSSMITH$JOHN$JIM$JR^1147 SOMESTREET^?',
    '%CALOS ANGELES^DOE$JOHN$CARL^14324 MAIN ST APT 5^?'
);

foreach ($licenses as $license) {
    preg_match(
        '@
            ^%
            (.{2})          # State, 2 chars
            ([^^]{0,12}.)   # City, 13 chars, delimited by ^
            ([^^]{0,34}.)   # Name, 35 chars, delimited by ^
            ([^^]{0,28}.)   # Address, 29 chars, delimited by ^
            \?$
        @x',
        $license,
        $fields
    );

    $state   = $fields[1];
    $city    = rtrim($fields[2], '^');
    $name    = explode('$', rtrim($fields[3], '^'));
    $address = explode('$', rtrim($fields[4], '^'));

    echo "$license\n";
    echo "STATE:   "; print_r($state);   echo "\n";
    echo "CITY:    "; print_r($city);    echo "\n";
    echo "NAME:    "; print_r($name);
    echo "ADDRESS: "; print_r($address);
    echo "\n";
}

输出:

CAMISSION HILLSSMITH$JOHN$JIM$JR^1147 SOMESTREET^
STATE:   CA
CITY:    MISSION HILLS
NAME:    Array
(
    [0] => SMITH
    [1] => JOHN
    [2] => JIM
    [3] => JR
)
ADDRESS: Array
(
    [0] => 1147 SOMESTREET
)

CALOS ANGELES^DOE$JOHN$CARL^14324 MAIN ST APT 5^
STATE:   CA
CITY:    LOS ANGELES
NAME:    Array
(
    [0] => DOE
    [1] => JOHN
    [2] => CARL
)
ADDRESS: Array
(
    [0] => 14324 MAIN ST APT 5
)

John,谢谢你。我还在努力让它工作。正则表达式不是我的强项。我应该删除“%”和“?”因为它们是不必要的。我只需要从你的正则表达式中删除这些字符就可以让它工作了吗? - John
@John 我不确定。你把 ^% 改成了 ^\?$ 改成了 $ - John Kugelman
好的,这就是将被传递到我的方法中的确切内容。CAMISSION HILLSSMITH$JOHN$JIM$JR^1147 SOMESTREET^ - John
约翰,你真是个好汉!说实话,如果没有这里大家的帮助,特别是你的帮助,我永远也无法完成这个。真的非常感谢你! - John
@John Kugelman:你的正则表达式太令人印象深刻了,我不得不开一个新的StackOverflow问题来解决我所有关于它的问题!!https://dev59.com/WFDTa4cB1Zd3GeqPI2qJ - gMale
显示剩余4条评论

3
你几小时前不是问过这个问题吗?有人在这里发布了一个正则表达式,处理分隔或刚好为13个字符的字符串情况:Help with a delimited string。那个方法行不通吗?
编辑:这个格式在这里被解释:http://en.wikipedia.org/wiki/Magnetic_stripe_card#United_States_driver.27s_licenses。对于城市,它说“字段分隔符-一个字符(通常为'^')(如果城市达到最大长度,则不存在)”。所以,一个简单的正则表达式就可以在这里发挥奇妙作用。请参考示例,您可以调整它以匹配此处详细说明的格式。
编辑:好的,我会尝试一下。
$str = "%CAMISSION HILLSSMITH$JOHN$JIM$JR^1147 SOMESTREET^?";
preg_match("/%(..)".
           "([^\^]{1,13})\^?".
           "([^\\\$]+)\\\$".
           "([^\\\$]+)\\\$/",
           $str, $m);
$State = $m[1];
$City = $m[2];
$LastName = $m[3];
$FirstName = $m[4];

以下是一个示例,说明如何实现它。 基本上,([^\^]{1,13}) 表示它将尝试获取最多 13 个不是 '^' 字符的字符。完成后,如果有'^'字符,则通过 \^? 消耗它本身。


谢谢提供链接。是的,那是我。我尝试了我所给出的代码,但它并没有起作用。我得到了错误的结果。我接受的答案和正则表达式做的事情是一样的。 - John
你需要根据格式进行调整,它不仅仅是13个字符。请参考我添加的维基百科链接。这种格式是明确定义的,但它不仅仅是“每个条目最多13个字符”。 - EboMike
好的,Ebo,再次感谢。我会尝试一下。你能确认我需要做的是100%可行的吗?我对字符串函数并不是很了解,正则表达式更不懂。 - John
1
这其实相当简单。顺便附上原始规格的链接。http://www.aamva.org/aamva/DocumentDisplay.aspx?id={966F5E02-58E1-4935-9F8D-9836A90217FE} ... 我可能会尝试使用正则表达式,至少从一开始就是。 - EboMike
谢谢Mike。那份文档很有帮助。我真希望在接手这个之前有时间看一下它。你认为John K.提供的解决方案怎么样? - John
显示剩余2条评论

2
从左到右处理一个字段。
去掉前面的%:
CAMISSION HILLSSMITH$JOHN$JIM$JR^1147 SOMESTREET^?
取前15个字符(第一个字段最多15个字符,对吧?):
CAMISSION HILLS
不包含插入符 - 太好了,这是我们的第一个字段 - 下一个字段从第16个字符开始:
SMITH$JOHN$JIM$JR^1147 SOMESTREET^? (R1)
我不知道这个字段的最大长度 - 让我们假设它是20。 取前20个字符:
SMITH$JOHN$JIM$JR^11
包含插入符 - 所以我们这里有> 1个字段。 取到插入符为止的字符:
SMITH$JOHN$JIM$JR
...那就是我们的下一个字段。 现在从上面的(R1)字符串中抓取从(前一个字段的长度+2)个字符开始的字符串(+2跳过^):
1147 SOMESTREET^?
等等。

谢谢,威尔。第一个字段是州,始终为2,这个很容易。第二个字段是城市,之后是人名。最后一个字段是完整地址。第一个字段是2个字符。城市字段是13个字符。姓名字段是35个字符,地址是29个字符。 - John
@John:没问题 - 很高兴能帮忙。虽然上面给出的方法不是最有效的方法,但它确实可以帮助您逻辑地将其分解为可管理的块 - 您应该会发现用这种方式在 PHP 中执行解析非常简单。 - Will A
Will,感谢你的帮助。@xkcd:我刚访问了你的网站,在主页上看到了正则表达式漫画条。我还在笑呢。 :) - John

0
如果这是Java,我会用正则表达式来解决。我知道PHP中一定也有类似的东西吧?
你提到的所有限制都可以转换成正则表达式。
例如:
X{n,m}?      X, at least n but not more than m times

可以与类似以下的内容一起使用:

[^%\$\^]{1,13}[%\$\^]

这段内容的含义是“1-13个字符,不能等于%、$或^,后面跟着这三个限定符之一”

当我写正则表达式时,经常参考Java的精彩文档页面。你也可以使用巧妙的技巧提取特定的匹配部分和提取特定的单词。虽然我更熟悉Java,但PHP作为一门成熟的语言,同样具备这些功能。

希望这能在某种程度上帮到你。如果没有其他人回答,我可以尝试创建你所需的正则表达式。

  • gMale(男性)

gmale,谢谢你的代码。我马上要试试看。 - John

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接