PHP 中的正则表达式:将字符串中第一个单词之后的所有单词截断为第一个字符
Regex in PHP: take all the words after the first one in string and truncate all of them to the first character
我的正则表达式非常糟糕。
我有一个字符串,里面可能有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+
并替换为:
.
RegEx Demo
代码:
$name = preg_replace('/^\w+(?:$| +)(*SKIP)(*F)|(\w)\w+/', '.', $name);
解释:
(*FAIL)
表现得像一个失败的否定断言,是 (?!)
的同义词
(*SKIP)
定义一个点,当子模式稍后失败时,正则表达式引擎不允许回溯超过该点
(*SKIP)(*FAIL)
一起提供了一个很好的限制替代方案,你不能在上面的正则表达式中有可变长度的回顾。
^\w+(?:$| +)(*SKIP)(*F)
匹配名称中的第一个单词并跳过它(什么都不做)
(\w)\w+
匹配所有其他单词并将其替换为第一个字母和一个点。
您可以使用积极的回顾断言。
(?<=\h)([A-Z])\w+
或
如果您想将 Bob F
转换为 Bob F.
,请使用此正则表达式
(?<=\h)([A-Z])\w*(?!\.)
然后将匹配到的字符替换为.
代码就像,
preg_replace('~(?<=\h)([A-Z])\w+~', '.', $string);
(?<=\h)([A-Z])
捕获前面有水平 space 字符的所有大写字母。
\w+
匹配一个或多个单词字符。
将匹配的字符替换为组索引 1
内的字符加上一个点将为您提供所需的输出。
只有前瞻和词边界检查的简单解决方案:
preg_replace('~(?!^)\b(\w)\w+~', '.', $string);
(\w)\w+
是名字中的一个词,第一个字符被捕获
(?!^)\b
执行单词边界检查 \b
,并确保匹配项不在字符串 (?!^)
. 的开头
我的正则表达式非常糟糕。
我有一个字符串,里面可能有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+
并替换为:
.
RegEx Demo
代码:
$name = preg_replace('/^\w+(?:$| +)(*SKIP)(*F)|(\w)\w+/', '.', $name);
解释:
(*FAIL)
表现得像一个失败的否定断言,是(?!)
的同义词
(*SKIP)
定义一个点,当子模式稍后失败时,正则表达式引擎不允许回溯超过该点(*SKIP)(*FAIL)
一起提供了一个很好的限制替代方案,你不能在上面的正则表达式中有可变长度的回顾。^\w+(?:$| +)(*SKIP)(*F)
匹配名称中的第一个单词并跳过它(什么都不做)(\w)\w+
匹配所有其他单词并将其替换为第一个字母和一个点。
您可以使用积极的回顾断言。
(?<=\h)([A-Z])\w+
或
如果您想将 Bob F
转换为 Bob F.
(?<=\h)([A-Z])\w*(?!\.)
然后将匹配到的字符替换为.
代码就像,
preg_replace('~(?<=\h)([A-Z])\w+~', '.', $string);
(?<=\h)([A-Z])
捕获前面有水平 space 字符的所有大写字母。\w+
匹配一个或多个单词字符。将匹配的字符替换为组索引 1
内的字符加上一个点将为您提供所需的输出。
只有前瞻和词边界检查的简单解决方案:
preg_replace('~(?!^)\b(\w)\w+~', '.', $string);
(\w)\w+
是名字中的一个词,第一个字符被捕获(?!^)\b
执行单词边界检查\b
,并确保匹配项不在字符串(?!^)
. 的开头