【问题标题】:Replacing a string while choosing the order in which the part of strings get replaced first在选择首先替换字符串部分的顺序时替换字符串
【发布时间】:2015-12-01 01:25:21
【问题描述】:

我正在尝试用某些单词替换字符串,但我也想按照数组中的位置顺序替换它。例如,我想替换“b c”,然后尝试替换“a b”,而不更改原始字符串中的位置。顺便说一句,这些字母应该代表实际的单词,它们不应该是另一个单词的一部分。例如,“sun”这个词是“sunflower”的一部分,“sunflower”这个词不能仅仅因为其中有“sun”这个词而被替换。

$text = "a b c";
$replacement = array("a b" => "ab","b c" => "bc");
$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);

第一个结果

ab c 

第二个结果

我改变了数组中的位置,认为它会影响字符串被替换的顺序。可悲的是,它不是那样工作的,我得到了相同的结果。

$replacement = array("b c" => "bc","a b" => "ab");
ab c

此时,我意识到不是数组的位置会影响首先替换字符串的哪一部分,而是显示在原始字符串上的字符串部分的顺序决定了顺序它被替换为。

所以,我的问题是,有没有办法以某种方式使其可以根据字符串在数组中的顺序或以不同的方式替换字符串?比如我想替换

"b c"

在我尝试替换“a b”而不更改原始字符串的位置之前。那可行吗?谢谢。

【问题讨论】:

  • 我想您将不得不更改正则表达式:添加一个否定的前瞻 a b(?! c\b)。见demo
  • @stribizhev 这是您更改的代码的唯一部分吗? $replacement = array("a b(?! c\b)" => "ab","b c" => "bc");请在下面的答案中解释您的负面预测,以及它是如何工作的。
  • 我只在确定这是解决问题的正确方法时才发布。否定前瞻是确保在前面的子模式匹配之后没有指定的模式。 a b(?! c\b) 永远不会匹配 a b 后跟一个空格,然后是整个单词 c。现在,问题是,这个解决方案可以外推到您的问题吗?有多少可能的上下文,替换数组有多大?如果您手动创建它,那么也许这是一个解决方案。
  • @stribizhev 老实说,我对您的方法有些困惑。也许如果您提供更详细的答案,我将能够使用它。

标签: php arrays regex string replace


【解决方案1】:

[编辑]

这个想法包括将原始文本转换为一个数组(开头有一个元素,即文本)。偶数索引处的数组项被拆分为每个模式。由于使用了PREG_SPLIT_DELIM_CAPTURE 选项,分隔符总是有一个奇数索引,一旦匹配就保持不变。

$text = 'a b c';
$rep = ['b c'=>'bc', 'a b'=>'ab', ];

$pats = array_map(function($i) {
    return '~\b(' . preg_quote($i, '~') . ')\b~';
}, array_keys($rep));

$parts = (array)$text; // or $parts = [ $text ]; // it's the same

foreach ($pats as $pat) {
    $temp = [];
    foreach ($parts as $k=>$part) {
        if ($k & 1)
            $temp[] = $part;
        else
            $temp = array_merge(
                $temp,
                preg_split($pat, $part, -1, PREG_SPLIT_DELIM_CAPTURE)
            );
    }
    $parts = $temp;
}

$result = '';
foreach ($parts as $k=>$part) {
    $result .= ($k & 1) ? $rep[$part] : $part;
}

echo $result;

【讨论】:

  • 佩德罗先回答,对不起。但我对你的答案投了赞成票!
  • Casimir et Hippolyte 的代码更好更简单。它应该被接受为最佳答案。
  • @PedroPinheiro 好吧。好的。当然。如果这就是你想要的。
  • 其实这和原来写代码的方式不一样。比如你把a=>b,然后b=>c,a会变成c,而不是停在b。原始代码确保不会发生这种情况。
  • @frosty:确实,它允许循环替换,(和str_replace一样),我不知道这是个问题。
【解决方案2】:

我更改了您的代码以表示(我认为)您想要的:

$text = "a b c a b";
$replacement = array("b c" => "bc", "a b" => "ab");
$search = array_map(function($v){
    return preg_quote($v, "/");
}, array_keys($replacement));

for($i = 0; $i < count($replacement); $i++) {
    $regex = "/\b(" . $search[$i] . ")\b/";
    echo $text = preg_replace_callback($regex, function($m)use($replacement){
        return $replacement[$m[1]];
    }, $text);
    echo "<br>";
}

基本上,我不是依靠正则表达式来完成这项工作,而是执行一个 for 循环来遍历每个替换并创建正则表达式。这样,数组的顺序很重要。我还更改了最初的$text 以测试它是否有效

【讨论】:

  • 其实这和原来写代码的方式不一样。例如,如果你把a=>b,然后b=>c,a会变成c,而不是停在b。原始代码确保不会发生这种情况。 Casimir 的代码也有同样的问题。
猜你喜欢
  • 2012-07-17
  • 2013-08-13
  • 2013-01-30
  • 2013-06-19
  • 2021-03-25
  • 1970-01-01
  • 2016-09-22
  • 2016-01-02
相关资源
最近更新 更多