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

时间:2018-01-11 22:17:06

标签: php string

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

考虑到这些测试用例:

  • Van Fleur,Pat
  • Smith,John K
  • Smith,John Jr。
  • Smith,Jose Jr

我正在尝试规范化使用以下格式的列表中的名称:Lastname,Firstname

测试用例的预期输出:

  • Van Fleur,Pat
  • 张,John
  • 张,John
  • 史密斯,圣何塞

我使用的是以下行,但似乎我只是将这些测试用例的一部分考虑在内。

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

我在正则表达方面并不擅长,所以真的没有冒险走上那条道路。

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

3 个答案:

答案 0 :(得分:1)

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

<?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);

答案 1 :(得分:0)

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

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

Pattern Demo

代码:(Demo

$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 | [^,]*$/,其中包含空替换字符串。这将使用更多步骤,但将避免执行不必要的替换。

代码:(Demo

$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);

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

答案 2 :(得分:-2)

试试这个:

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