将字符串按非字母数字字符和数字与非数字字符之间的位置拆分

4
我正在尝试使用非字母数字的分隔符以及数字和非数字的交替方式拆分字符串。最终结果应该是一个由字母字符串和数字字符串组成的扁平数组。
我正在使用PHP,并希望使用正则表达式。
例如: - "ES-3810/24MX" 应变为 ['ES', '3810', '24', 'MX'] - "CISCO1538M" 应变为 ['CISCO' , '1538', 'M']
输入文件序列可以是数字或字母,分隔符可以是非字母数字字符,也可以是数字序列和字母序列之间的切换。

你所接受的答案中有一个bug。 - tchrist
3个回答

3
匹配所有正则表达式的命令是 preg_match_all(),它会输出一个多维数组的结果。这个正则表达式非常简单... 任何数字 ([0-9]) 出现一次或多次 (+) 或者 (|) 任何字母 ([A-z]) 出现一次或多次 (+)。请注意大写字母 A 和小写字母 z,以包含所有大写和小写字母。

文本框和 php 标签是为了方便而包含的,这样你就可以将其插入到你的 php 文件中并查看结果。

<textarea style="width:400px; height:400px;">
<?php

foreach( array(
        "ES-3810/24MX",
        "CISCO1538M",
        "123ABC-ThatsHowEasy"
    ) as $string ){

    // get all matches into an array
    preg_match_all("/[0-9]+|[[:upper:][:lower:]]+/",$string,$matches);

    // it is the 0th match that you are interested in...
    print_r( $matches[0] );

}

?>
</textarea>

在文本区域中输出的内容为:
Array
(
    [0] => ES
    [1] => 3810
    [2] => 24
    [3] => MX
)
Array
(
    [0] => CISCO
    [1] => 1538
    [2] => M
)
Array
(
    [0] => 123
    [1] => ABC
    [2] => ThatsHowEasy
)

错误答案,请停止使用 [A-z],该字符集包含您不需要的内容并且缺少您需要的内容。大写字母是 \p{Lu},小写字母是 \p{Ll} - tchrist
当然,你是正确的。我已经更新了我的答案,希望你喜欢我的[[:upper:][:lower:]]多于我的[A-z] - Billy Moon
没问题,我的数据库中只有大写字母。这并没有让我烦恼。 - mlh
为什么有人会使用[[:lower:]]而不是\p{Ll}?它的长度是两倍。是的,从技术上讲,\p{Lowercase=True}\p{General_Category=Lowercase_Letter}之间存在差异,但我相当怀疑PHP能够捕获到这种区别。 - tchrist
它是[:lower:]而不是[[:lower:]],因此实际上只有1.5倍长。我认为人们会使用它的原因是它更易读 - 这是良好代码的一个被低估的特性。 - Billy Moon
显示剩余2条评论

1

产生所需的平面输出数组的最直接的preg_函数是preg_split()

因为不管在非字母数字字符序列的两侧有什么组合,你都可以贪婪地在非字母数字子字符串上分割而不必“环顾四周”。

处理完这个初步障碍之后,然后在数字和非数字之间的零长度位置或非数字和数字之间的零长度位置上进行分割。

/             #starting delimiter
[^a-z\d]+     #match one or more non-alphanumeric characters
|             #OR
\d\K(?=\D)    #match a number, then forget it, then lookahead for a non-number
|             #OR
\D\K(?=\d)    #match a non-number, then forget it, then lookahead for a number
/             #ending delimiter
i             #case-insensitive flag

代码:(示例)

var_export(
    preg_split('/[^a-z\d]+|\d\K(?=\D)|\D\K(?=\d)/i', $string, 0, PREG_SPLIT_NO_EMPTY)
);

preg_match_all() 不是一种愚蠢的技术,但它不会返回数组,而是返回匹配次数,并生成一个包含二维数组的引用变量,需要访问第一个元素。可以承认的是,这种模式更短且更易于理解。(演示

var_export(
    preg_match_all('/[a-z]+|\d+/i', $string, $m) ? $m[0] : []
);

1
$str = "ES-3810/24MX35 123 TEST 34/TEST";
$str = preg_replace(array("#[^A-Z0-9]+#i","#\s+#","#([A-Z])([0-9])#i","#([0-9])([A-Z])#i"),array(" "," ","$1 $2","$1 $2"),$str);
echo $str;
$data = explode(" ",$str);
print_r($data);

我想不出更“简洁”的方式。


我不支持对输入字符串进行四次单独的遍历(使用四种不同的模式)来注入空格,然后在空格上进行分割。这个答案解释过于冗长,效率低下,缺乏灵感。 - mickmackusa

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