【问题标题】:Replacing Placeholder Variables in a String替换字符串中的占位符变量
【发布时间】:2013-04-02 20:05:40
【问题描述】:

刚刚完成这个功能。基本上,它假设查看一个字符串并尝试找到任何占位符变量,这些变量将放置在两个大括号{} 之间。它获取大括号之间的值,并使用它来查看应该匹配键的数组。然后它将字符串中的大括号变量替换为匹配键的数组中的值。

但它有一些问题。首先是当我var_dump($matches) 时,它会将结果放入一个数组中,在一个数组内。所以我必须使用两个foreach() 才能获得正确的数据。

我也觉得它很重,我一直在检查它,试图让它变得更好,但我有点难过。我错过了哪些优化?

function dynStr($str,$vars) {
    preg_match_all("/\{[A-Z0-9_]+\}+/", $str, $matches);
    foreach($matches as $match_group) {
        foreach($match_group as $match) {
            $match = str_replace("}", "", $match);
            $match = str_replace("{", "", $match);
            $match = strtolower($match);
            $allowed = array_keys($vars);
            $match_up = strtoupper($match);
            $str = (in_array($match, $allowed)) ? str_replace("{".$match_up."}", $vars[$match], $str) : str_replace("{".$match_up."}", '', $str);
        }
    }
    return $str;
}

$variables = array("first_name"=>"John","last_name"=>"Smith","status"=>"won");
$string = 'Dear {FIRST_NAME} {LAST_NAME}, we wanted to tell you that you {STATUS} the competition.';
echo dynStr($string,$variables);
//Would output: 'Dear John Smith, we wanted to tell you that you won the competition.'

【问题讨论】:

  • 请向我们提供数据样本($str & $vars)
  • 你的方法效率很低。考虑一种替代方法:a) 使用preg_replace_callback$vars 返回匹配的令牌值,而无需一百万次str_replace 调用; b) 转换$vars 中的每个条目以包含前导/后括号,然后将$vars 输入strtr
  • 您这样做是为了学习还是为了生产?如果用于生产,您可能需要查看模板库,例如 smarty
  • @DCoder:你能提供一些例子吗?我被你说的弄糊涂了。
  • @dm03514 它是用于生产的,但它就像初稿一样。不是最终产品。它也不适用于模板。这是一份时事通讯。

标签: php preg-match


【解决方案1】:

我认为对于这样一个简单的任务你不需要使用 RegEx:

$variables = array("first_name"=>"John","last_name"=>"Smith","status"=>"won");
$string = 'Dear {FIRST_NAME} {LAST_NAME}, we wanted to tell you that you {STATUS} the competition.';

foreach($variables as $key => $value){
    $string = str_replace('{'.strtoupper($key).'}', $value, $string);
}

echo $string; // Dear John Smith, we wanted to tell you that you won the competition.

【讨论】:

  • 这实际上比我的答案更简单,而且可能确实足够了。
  • 哇,是的。我想我们都想太多了。我会进行一些测试,看看这是否更快并让你知道。足够了。
  • @HamZaDzCyber​​DeV 我太想“漂亮”了,以至于没有想到像你这样的解决方案。有时先退后一步会有所帮助。不过,我刚刚想到的唯一一个小警告是,它不利于从输入字符串中删除无效占位符(但这不是一开始的要求)。
  • 提示:str_replace 可以获取字符串数组来查找/替换。一个str_replace 调用可能会超过一百个。
  • @DCoder 是的,你是对的,这里的问题是 $variables 中的键格式不正确(括在 {} 和大写之间)。
【解决方案2】:

我希望我加入派对还不算太晚——我会这样做:

function template_substitution($template, $data)
{
    $placeholders = array_map(function ($placeholder) {
        return strtoupper("{{$placeholder}}");
    }, array_keys($data));

    return strtr($template, array_combine($placeholders, $data));
}

$variables = array(
    'first_name' => 'John',
    'last_name' => 'Smith',
    'status' => 'won',
);

$string = 'Dear {FIRST_NAME} {LAST_NAME}, we wanted to tell you that you have {STATUS} the competition.';

echo template_substitution($string, $variables);

而且,如果您有任何机会可以使您的 $variables 键与您的占位符完全匹配,那么解决方案将变得非常简单:

$variables = array(
    '{FIRST_NAME}' => 'John',
    '{LAST_NAME}' => 'Smith',
    '{STATUS}' => 'won',
);

$string = 'Dear {FIRST_NAME} {LAST_NAME}, we wanted to tell you that you have {STATUS} the competition.';

echo strtr($string, $variables);

(参见 PHP 手册中的strtr()。)

考虑到 PHP 语言的性质,我相信这种方法应该会在该线程中列出的所有方法中产生最佳性能。


编辑: 7 年后重新审视这个答案后,我注意到我身边有一个潜在的危险疏忽,这也是 pointed out by another user。一定要以支持的形式拍拍他们的后背!

如果您对编辑前的答案感兴趣,请查看the revision history

【讨论】:

  • 我同意 - 除了性能,这也是最易读和维护的恕我直言。我对另一个问题 (stackoverflow.com/a/36781566/224707) 的回答也显示了这种方法的其他优点,例如能够在处理之前以编程方式增强替换值(例如编码或主题化)...
【解决方案3】:

我认为您可以通过以下方式大大简化您的代码(除非我误解了某些要求):

$allowed = array("first_name"=>"John","last_name"=>"Smith","status"=>"won");

$resultString = preg_replace_callback(

    // the pattern, no need to escape curly brackets
    // uses a group (the parentheses) that will be captured in $matches[ 1 ]
    '/{([A-Z0-9_]+)}/',

    // the callback, uses $allowed array of possible variables
    function( $matches ) use ( $allowed )
    {
        $key = strtolower( $matches[ 1 ] );
        // return the complete match (captures in $matches[ 0 ]) if no allowed value is found
        return array_key_exists( $key, $allowed ) ? $allowed[ $key ] : $matches[ 0 ];
    },

    // the input string
    $yourString
);

PS.:如果要从输入字符串中删除不允许的占位符,请替换

return array_key_exists( $key, $allowed ) ? $allowed[ $key ] : $matches[ 0 ];

return array_key_exists( $key, $allowed ) ? $allowed[ $key ] : '';

【讨论】:

  • 看起来可能会更好,但我很好奇function( $matches ) use ( $allowed ) 是什么?我从未见过“使用”,我的代码编辑器(dreamweaver)说它不正确。
  • @TylerDusty:这是一个anonymous function,需要 PHP 5.3。
  • @TylerDusty 回调是closureuse 构造用于从定义范围导入变量,以便它可以在闭包内使用。事实上,正如 DCoder 已经提到的,它是 PHP 5.3+ 的功能。
【解决方案4】:

提醒未来登陆此页面的人:使用foreach 循环和/或str_replace 方法的所有答案(包括已接受的答案)都容易被替换为good ol' Johnny { STATUS} 的名称带有Johnny won

Decent Dabbler 的 preg_replace_callback 方法和 U-D13 的第二个选项(但不是第一个)是我看到目前发布的唯一不受此影响的选项,但由于我没有足够的声誉来添加评论我想我会写一个完全不同的答案。

如果您的替换值包含用户输入,更安全的解决方案是使用 strtr 函数而不是 str_replace 以避免重新替换可能出现在您的值中的任何占位符。

$string = 'Dear {FIRST_NAME} {LAST_NAME}, we wanted to tell you that you {STATUS} the competition.';
$variables = array(
    "first_name"=>"John",
    // Note the value here
    "last_name"=>"{STATUS}",
    "status"=>"won"
);

// bonus one-liner for transforming the placeholders
// but it's ugly enough I broke it up into multiple lines anyway :)
$replacement = array_combine(
    array_map(function($k) { return '{'.strtoupper($k).'}'; }, array_keys($variables)),
    array_values($variables)
);

echo strtr($string, $replacement);

输出:Dear John {STATUS}, we wanted to tell you that you won the competition. 而 str_replace 输出:Dear John won, we wanted to tell you that you won the competition.

【讨论】:

  • strtr() 是这个问题的正确答案。
【解决方案5】:

这是我使用的函数:

function searchAndReplace($search, $replace){
    preg_match_all("/\{(.+?)\}/", $search, $matches);

    if (isset($matches[1]) && count($matches[1]) > 0){
        foreach ($matches[1] as $key => $value) {
            if (array_key_exists($value, $replace)){
                $search = preg_replace("/\{$value\}/", $replace[$value], $search);
            }
        }
    }
    return $search;
}


$array = array(
'FIRST_NAME' => 'John',
'LAST_NAME' => 'Smith',
'STATUS' => 'won'
);

$paragraph = 'Dear {FIRST_NAME} {LAST_NAME}, we wanted to tell you that you {STATUS} the competition.';

// outputs: Dear John Smith, we wanted to tell you that you won the competition.

只需将一些文本传递给它以进行搜索,以及一个包含替换的数组。

【讨论】:

    【解决方案6】:
    /**
       replace placeholders with object
    **/
    $user = new stdClass();
    $user->first_name = 'Nick';
    $user->last_name = 'Trom';
    
    $message = 'This is a {{first_name}} of a user. The user\'s {{first_name}} is replaced as well as the user\'s {{last_name}}.';
    
    preg_match_all('/{{([0-9A-Za-z_]+)}}/', $message, $matches);
    
    foreach($matches[1] as $match)
    {
        if(isset($user->$match))
            $rep = $user->$match;
        else
            $rep = '';
    
        $message = str_replace('{{'.$match.'}}', $rep, $message);
    }
    
    echo $message;
    

    【讨论】:

      猜你喜欢
      • 2012-05-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-26
      • 1970-01-01
      • 1970-01-01
      • 2020-06-03
      • 1970-01-01
      相关资源
      最近更新 更多