【问题标题】:Replacing letters simultaneously with strtr用strtr同时替换字母
【发布时间】:2015-09-04 02:15:00
【问题描述】:

我试图同时用 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”这样的词;

【问题讨论】:

  • 字符串中的空格数量不一致" a b c "
  • 和以前一样的问题:空格太多。为什么你的数组中有这些额外的空间?这就是导致您的问题的原因
  • @CodeGodie 确保这些词不是另一个词的一部分。例如,单词“no”是“nobody”的一部分,如果你替换了字符串中的所有“no”,它也会替换像“nobody”这样的单词;
  • 明白了。您应该编辑您的问题并将其放在那里,这将有助于我们为您提供更好的答案
  • @CodeGodie 好的。我更新了它。现在看起来有点复杂。

标签: php regex replace preg-replace strtr


【解决方案1】:

那么这里发生了什么?

$text = " a b c ";
$text = strtr($text, array(" a " => " b ", " b " => " a ", " c " => " d "));

首先你必须知道:

  1. 将首先替换最长的键
  2. 替换后不再搜索此键

所以这里所有的键都具有相同的长度,这意味着它将替换第一个 key => value 元素,然后是第二个,依此类推。

所以首先 " a " => " b " 将被替换,这将改变你的起始字符串:

 a b c 
↑↑↑ Match

到这里:

 b b c
↑↑↑ Replacement

还记得我们说过,替换值后它不再搜索它吗?确切地!现在它搜索" b " => " a ",但只在这部分字符串中:

 a b c 

所以它不会找到它,因为 b 前面没有空间可以搜索。这就是为什么你没有得到预期的输出。并且c 会再次被找到,因为它前面有一个空格。

因此,您的代码中的问题是,它在搜索下一个键时不包含替换值。即使[space]b[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);

?>

【讨论】:

  • 我该如何解决?另请阅读我的新更新,以及为什么我在单词周围包含空格。
  • 看起来那 40K 的声望不是白费的!你真聪明!谢谢!
  • 此答案假定要替换的字符串仅由单词字符组成。这是一个公平的假设,但我认为明确说明该假设会很好,因为当要替换的字符串以非单词字符开始/结束或包含正则表达式元字符时,行为会变得不稳定。
  • @nhahtdh 因为在 OP 的代码中没有必要,所以我没有逃避它们。刚刚更新了我的答案,以防万一
  • @jessica 两者都会,但如果涉及正则表达式元字符,例如dot . 那么只有新版本才能正常工作。
【解决方案2】:

没有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 中使用空字符串。

【讨论】:

    猜你喜欢
    • 2017-05-16
    • 2017-11-05
    • 2016-12-22
    • 1970-01-01
    • 1970-01-01
    • 2012-06-24
    • 2012-11-10
    • 1970-01-01
    • 2013-11-04
    相关资源
    最近更新 更多