我对正则表达式很糟糕。
我有一个字符串,里面可能有1个或多个单词(通常是2个或3个),通常是人名,例如:
$str1 = 'John Smith';
$str2 = 'John Doe';
$str3 = 'David X. Cohen';
$str4 = 'Kim Jong Un';
$str5 = 'Bob';
我想按如下方式转换每个:
$str1 = 'John S.';
$str2 = 'John D.';
$str3 = 'David X. C.';
$str4 = 'Kim J. U.';
$str5 = 'Bob';
我的猜测是我应该首先匹配第一个单词,如下所示:
preg_match( "^([\w\-]+)", $str1, $first_word )
然后是第一个单词之后的所有单词...但是我如何匹配这些单词呢?我应该再次使用 preg_match 并在参数中使用 offset = 1 吗?但该偏移量是以字符或字节为单位的,对吗?
无论如何,在我匹配第一个后面的单词之后,如果存在,我应该为每个单词做类似的事情:
$second_word = substr( $following_word, 1 ) . '. ';
或者我的做法完全错误?
谢谢
ps - 当字符串包含三个或更多单词时,如果正则表达式可以保留整个前两个单词,那将是一个福音......(例如“Kim Jong U.”)。
可以使用正则表达式在单个
preg_replace
中完成。
您可以使用此正则表达式进行搜索:
^\w+(?:$| +)(*SKIP)(*F)|(\w)\w+
并替换为:
$1.
代码:
$name = preg_replace('/^\w+(?:$| +)(*SKIP)(*F)|(\w)\w+/', '$1.', $name);
说明:
(*FAIL)
的行为类似于失败的否定断言,是 (?!)
(*SKIP)
定义了一个点,当子模式稍后失败时,正则表达式引擎将不允许回溯到该点(*SKIP)(*FAIL)
一起提供了一个很好的限制替代方案,即在上面的正则表达式中不能有可变长度的lookbehind。^\w+(?:$| +)(*SKIP)(*F)
匹配名称中的第一个单词并跳过它(不执行任何操作)(\w)\w+
匹配所有其他单词,并将其替换为第一个字母和一个点。仅具有前瞻和字边界检查的简单解决方案:
preg_replace('~(?!^)\b(\w)\w+~', '$1.', $string);
(\w)\w+
是名称中的一个单词,捕获第一个字符(?!^)\b
执行单词边界检查 \b
,并确保匹配项不在字符串的开头 (?!^)
。只需从字符串的最后一个(非第一个)单词的第二个字符开始匹配 - 然后用点替换该匹配(不需要捕获或引用)。
匹配文字空格,然后
\w
匹配单词字符,然后用 \K
忘记这些字符,然后匹配零个或多个单词字符,直到字符串末尾,以确保所有姓氏在第一个字母后被截断并在末尾添加一个点。
代码:(演示)
$strings = [
'John Smith',
'John Doe',
'David X. Cohen',
'Kim Jong Un',
'Bob',
];
var_export(
preg_replace('/ \w\K\w*$/', '.', $strings)
);