如何在 PHP 中规范化字符串以获得所需的输出?



在PHP中规范化某些字符串时遇到一些麻烦...

给定这些测试用例:

  • 范芙蓉,帕特
  • 史密斯,约翰·
  • 史密斯、小约翰·
  • 史密斯,小何塞

我正在尝试规范使用以下格式的列表中的名称:姓氏,名字

测试用例的预期输出:

  • 范芙蓉,帕特
  • 史密斯,约翰
  • 史密斯,约翰
  • 史密斯,何塞

我正在使用以下行,但似乎我只考虑了这些测试用例的一个子集。

使用这个:strtok(trim(strtolower($name)), ' ')

我不擅长正则表达式,所以真的还没有冒险走这条路。

你能帮助我使用正则表达式或本机函数实现所需的输出吗?

没办法解决这个问题,你需要以某种方式迭代该数据数组并转换每个条目:

<?php
$data = [
'Van Fleur, Pat', 
'Smith,John K', 
'Smith, John Jr.', 
'Smith,Jose Jr'
];
array_walk($data, function($value, $key) use (&$data) {
preg_match('|s*(w.+),s*(w+)|', $value, $token);
$data[$key] = sprintf('%s,%s', $token[1], $token[2]);
});
print_r($data);

输出显然是:

Array
(
[0] => Van Fleur,Pat
[1] => Smith,John
[2] => Smith,John
[3] => Smith,Jose
)

一个明显的替代方案是这样的:

<?php
$input = [
'Van Fleur, Pat', 
'Smith,John K', 
'Smith, John Jr.', 
'Smith,Jose Jr'
];
$output =  array_map(function($value) {
preg_match('|s*(w.+),s*(w+)|', $value, $token);
return sprintf('%s,%s', $token[1], $token[2]);
}, $input);
print_r($output);

但在这里要小心,这种方法不会很好地扩展,因为您实际上以这种方式使数据的内存占用量翻倍......


因此,也许该替代方案会更优雅,因为就像第一个示例一样,它对条目进行了就地更改:

<?php
$data = [
'Van Fleur, Pat', 
'Smith,John K', 
'Smith, John Jr.', 
'Smith,Jose Jr'
];
foreach($data as &$entry) {
preg_match('|s*(w.+),s*(w+)|', $entry, $token);
$entry = sprintf('%s,%s', $token[1], $token[2]);
}
print_r($data);

考虑到您在下面的评论中描述了略有不同的情况,我会添加此建议:

$entry = preg_replace('|^s*(w.+),s*(w+)s*.*$|', '$1,$2', $entry);

捕获前导子字符串直到,,然后匹配(但不捕获)逗号和可选空格,然后贪婪地捕获非空格字符,然后只匹配字符串的其余部分,以便替换值覆盖完整的原始值。

使用否定字符类可加快模式。 下面是一个简单的单调用方法:

模式演示

代码:(演示)

$names=[
'Van Fleur, Pat', 
'Smith,John K', 
'Smith, John Jr.', 
'Smith,Jose Jr'
];
$names=preg_replace('/([^,]+), ?([^ ]+).*/','$1,$2',$names);
var_export($names);

输出:

array (
0 => 'Van Fleur,Pat',
1 => 'Smith,John',
2 => 'Smith,John',
3 => 'Smith,Jose',
)

让我们考虑一些更复杂的假设输入 - 包括不需要更正的名称。

Van Fleur, Pat                          // <-- 1 replacement
Smith,Josiah                            // <-- nothing to fix
Smith,John K                            // <-- 1 replacement
Smith,John Jacob Jingleheimer           // <-- 1 long replacement
O'Shannahan-O'Neil, Sean Patrick Eamon  // <-- double surname with apostrophes
de la Cruz, Bethania                    // <-- 3-word surname
Smith, John Jr.                         // <-- 2 replacements
Smith,Jose Jr                           // <-- 1 replacement

您可以使用我发布的第一个模式,这是一种有效的模式,但它会对不需要任何修复的名称执行替换。

或者,您可以使用这种"无捕获"模式:/,K | [^,]*$/使用空替换字符串。 这将使用更多步骤,但将避免执行不必要的替换。

代码:(演示)

$names=preg_replace('/,K | [^,]*$/','',$names);
var_export($names);

输出:

array (
0 => 'Van Fleur,Pat',
1 => 'Smith,Josiah',
2 => 'Smith,John',
3 => 'Smith,John',
4 => 'O'Shannahan-O'Neil,Sean',
5 => 'de la Cruz,Bethania',
6 => 'Smith,John',
7 => 'Smith,Jose',
)

最后,如果你对正则表达式有一些根深蒂固的仇恨(我当然没有),你可以使用这种方法:

foreach($names as &$name){
$parts=explode(',',$name);
$name=$parts[0].','.explode(' ',ltrim($parts[1]),2)[0];
}
unset($name);  // this is not required, but many recommend it to prevent issues downscript
var_export($names);

关于哪一个最适合您的项目的决定将取决于您的真实数据的质量和您的个人品味。 如果优化是优先事项,我建议运行一些比较速度测试。

试试这个:

^([^,]+),s?([^s]+)

最新更新