用 strtr 同时替换字母
Replacing letters simultaneously with strtr
我正在尝试用 strtr 同时替换一个字符串,但我遇到了一个问题。如果同时被替换的字母紧挨着另一个同时被替换的字母,则它不会替换第一个字母之后的字母。我该怎么做才能解决这个问题?谢谢
$text = " no bacon cats nobody ";
$text = strtr($text, array(" no " => " bacon ", " bacon " => " no ", " cats " => " dogs "));
echo $text;
预期结果
bacon no dogs nobody
实际结果
bacon bacon dogs nobody
P.S:我必须确保这些词不是另一个词的一部分。这就是那里有空白的原因。例如,单词 "no" 是 "nobody" 的一部分,如果您替换字符串中的所有 "no",它也会替换像 "nobody" 这样的单词;
那么这里发生了什么?
$text = " a b c ";
$text = strtr($text, array(" a " => " b ", " b " => " a ", " c " => " d "));
首先你必须知道:
- 最长的密钥将首先被替换
- 替换后不再搜索此密钥
所以这里所有键的长度都相同,这意味着它将替换第一个 key => value
元素,然后是第二个,依此类推。
所以首先 " a " => " b "
将被替换,这将更改您的起始字符串:
a b c
↑↑↑ Match
对此:
b b c
↑↑↑ Replacement
还记得我们怎么说的,替换值后它不再搜索它了吗?确切地!现在它搜索 " b " => " a "
,但只搜索字符串的这一部分:
a b c
所以它不会找到它,因为 b 前面没有要搜索的 space。这就是为什么您没有获得预期输出的原因。 c
会再次被找到,因为它前面有一个 space。
所以您的代码中的问题是,它在搜索下一个键时不包括替换值。即使 [space]b[space]
在您的字符串中,它也找不到它,因为第一个 space 来自键 [space]a[space]
的替换值,它不会包含在下一个中搜索。
编辑:
从你更新的问题来看,你这样做似乎是为了防止单词成为其他单词的一部分。要解决这个问题,只需创建一个查找数组并使用带有单词边界的 preg_replace_callback()
来仅匹配完整的单词,而不仅仅是其中的一部分,例如
<?php
$text = " apple bacon cats ";
$replacement = ["apple" => "bacon", "bacon" => "apple", "cats" => "dogs"];
$search = array_map(function($v){
return preg_quote($v, "/");
}, array_keys($replacement));
echo $text = preg_replace_callback("/\b(" . implode("|", $search) . ")\b/", function($m)use($replacement){
return $replacement[$m[1]];
}, $text);
?>
没有strtr的方法:
$text = 'no bacon cats nobody';
$rep = ['no'=>'bacon', 'bacon'=>'no', 'cats'=>'dogs'];
$parts = explode(' ', $text);
foreach ($parts as &$part) {
if (isset($rep[$part]))
$part = $rep[$part];
}
$result = implode(' ', $parts);
如果您想要更灵活的东西,请将 explode
行替换为:
$parts = preg_split('~\b~', $parts);
并使用带有 implode
的空字符串。
我正在尝试用 strtr 同时替换一个字符串,但我遇到了一个问题。如果同时被替换的字母紧挨着另一个同时被替换的字母,则它不会替换第一个字母之后的字母。我该怎么做才能解决这个问题?谢谢
$text = " no bacon cats nobody ";
$text = strtr($text, array(" no " => " bacon ", " bacon " => " no ", " cats " => " dogs "));
echo $text;
预期结果
bacon no dogs nobody
实际结果
bacon bacon dogs nobody
P.S:我必须确保这些词不是另一个词的一部分。这就是那里有空白的原因。例如,单词 "no" 是 "nobody" 的一部分,如果您替换字符串中的所有 "no",它也会替换像 "nobody" 这样的单词;
那么这里发生了什么?
$text = " a b c ";
$text = strtr($text, array(" a " => " b ", " b " => " a ", " c " => " d "));
首先你必须知道:
- 最长的密钥将首先被替换
- 替换后不再搜索此密钥
所以这里所有键的长度都相同,这意味着它将替换第一个 key => value
元素,然后是第二个,依此类推。
所以首先 " a " => " b "
将被替换,这将更改您的起始字符串:
a b c
↑↑↑ Match
对此:
b b c
↑↑↑ Replacement
还记得我们怎么说的,替换值后它不再搜索它了吗?确切地!现在它搜索 " b " => " a "
,但只搜索字符串的这一部分:
ab c
所以它不会找到它,因为 b 前面没有要搜索的 space。这就是为什么您没有获得预期输出的原因。 c
会再次被找到,因为它前面有一个 space。
所以您的代码中的问题是,它在搜索下一个键时不包括替换值。即使 [space]b[space]
在您的字符串中,它也找不到它,因为第一个 space 来自键 [space]a[space]
的替换值,它不会包含在下一个中搜索。
编辑:
从你更新的问题来看,你这样做似乎是为了防止单词成为其他单词的一部分。要解决这个问题,只需创建一个查找数组并使用带有单词边界的 preg_replace_callback()
来仅匹配完整的单词,而不仅仅是其中的一部分,例如
<?php
$text = " apple bacon cats ";
$replacement = ["apple" => "bacon", "bacon" => "apple", "cats" => "dogs"];
$search = array_map(function($v){
return preg_quote($v, "/");
}, array_keys($replacement));
echo $text = preg_replace_callback("/\b(" . implode("|", $search) . ")\b/", function($m)use($replacement){
return $replacement[$m[1]];
}, $text);
?>
没有strtr的方法:
$text = 'no bacon cats nobody';
$rep = ['no'=>'bacon', 'bacon'=>'no', 'cats'=>'dogs'];
$parts = explode(' ', $text);
foreach ($parts as &$part) {
if (isset($rep[$part]))
$part = $rep[$part];
}
$result = implode(' ', $parts);
如果您想要更灵活的东西,请将 explode
行替换为:
$parts = preg_split('~\b~', $parts);
并使用带有 implode
的空字符串。