PHP preg_replace在最后一个下划线后出现任何文本

时间:2012-04-19 20:13:27

标签: php regex preg-replace preg-match

我可以使用explode执行此操作(或与strrpos一起使用),但我更喜欢使用preg_replace因为我应该更快一点,我认为(不是吗) ?)。此外,它简洁而优雅。

目的是,给定一个像a_b_c这样的字符串来获取另一个强烈的,其中最后一个_ 后面的字符替换为传递的字符串。

我不擅长正则表达。我有时间在学习上买一本好书。无论如何,我已经尝试了这个正则表达式'/_(.*)$/',因为匹配字符串的结尾,捕获最后一个下划线之后的任何字符。

我的论证有什么问题?

// Do it using explode
function foo($string, $replacement)
{
    $pieces = explode('_', $string);
    array_pop($pieces);
    return implode('_', array_merge($pieces, array($replacement)));
}

// Do it using regular expression (not working)
function bar($string, $replacement)
{
    return preg_replace('/_(.*)$/', $replacement, $string);
}

echo foo('a_b_c', 3); // Prints a_b_3
echo bar('a_b_c', 3); // Prints a3 wrong!!!

7 个答案:

答案 0 :(得分:3)

你很亲密,你正在寻找的模式是:

([^_]*)$

这将仅匹配不是下划线的文本,尽可能接近字符串的末尾。它也不包括下划线,因为您的替换字符串不使用$1来指定匹配的组。

答案 1 :(得分:2)

根据您的常用搜索字符串和您的PCRE版本,preg_replacestrrpos可能最佳:

功能

function usingExplode($string, $replacement) {
    $pieces = explode('_', $string);
    array_pop($pieces);
    return implode('_', array_merge($pieces, array($replacement)));
}

function usingStrrpos($string, $replacement) {
    return substr($string, 0, strrpos($string, '_') + 1) . $replacement;
}

function usingPreg($string, $replacement) {
    return preg_replace('/_[^_]*$/', '_' . $replacement, $string);
}

测试工具

function speedTest($function, $string, $count = 100000) {
    $start = microtime(true);

    for ($i = 0; $i < $count; $i++) {
        $function($string, 'replacement');
    }

    $end = microtime(true);

    printf('%-12s: %01.2fs%s', $function, $end - $start, PHP_EOL);
}

$tests = array('a_b_c', 'abcdefghijklmnopqrstuvwxy_z', 'a_bcdefghijklmnopqrstuvwxyz', 'a_b_c_d_e_f_g_h_i_j_k_l_m_n_o_p_q_r_s_t_u_v_w_x_y_z');

foreach ($tests as $test) {
    echo $test . ':' . PHP_EOL;
    speedTest('usingExplode', $test);
    speedTest('usingStrrpos', $test);
    speedTest('usingPreg',    $test);
    echo PHP_EOL;
}

结果

a_b_c:
usingExplode: 0.64s
usingStrrpos: 0.34s
usingPreg   : 0.33s

abcdefghijklmnopqrstuvwxy_z:
usingExplode: 0.61s
usingStrrpos: 0.32s
usingPreg   : 0.32s

a_bcdefghijklmnopqrstuvwxyz:
usingExplode: 0.60s
usingStrrpos: 0.32s
usingPreg   : 0.32s

a_b_c_d_e_f_g_h_i_j_k_l_m_n_o_p_q_r_s_t_u_v_w_x_y_z:
usingExplode: 1.39s
usingStrrpos: 0.32s
usingPreg   : 0.71s

请注意(至少在我使用PHP 5.4.0进行设置时),preg_replace会为strrpos提供资金,直到最后一个下方有大量下划线。< / p>

编辑:我将bfrohs's regex插入套件中,除非要替换的下划线靠近字符串的开头,否则效果不佳:

a_b_c:
usingPreg2: 0.40s

abcdefghijklmnopqrstuvwxy_z:
usingPreg2: 1.91s

a_bcdefghijklmnopqrstuvwxyz:
usingPreg2: 0.38s

a_b_c_d_e_f_g_h_i_j_k_l_m_n_o_p_q_r_s_t_u_v_w_x_y_z:
usingPreg2: 1.04s

答案 2 :(得分:1)

问题是第一个下划线是匹配的。您需要在下划线之外的任何内容下划线:

'/_([^_]*)$/'

答案 3 :(得分:1)

常规表达式/_(.*)$/匹配下划线,后跟任何文本,后跟字符串的结尾。没有任何东西可以保留包含下划线的“任何文本”,默认情况下,匹配器将选择最左边,最长的匹配。所以在'a_b_c'中它恰好在'a'之后匹配。

您可以通过将.(匹配任何字符)替换为字符类[^_]来修复此问题,该字符类匹配下划线之外的任何字符。

此外,由于您没有对捕获的组执行任何操作,因此不需要括号。因为,根据您的示例,您不想替换下划线本身,您应该将它从正则表达式中删除。

function bar($string, $replacement)
{
    return preg_replace('/[^_]*$/', $replacement, $string, 1);
}

答案 4 :(得分:1)

如果考虑速度,使用strpos将比preg_replace更快。所有字符串函数(据我所知)比正则表达式函数慢。

以下是速度测试之一:http://lzone.de/articles/php-string-search.htm

答案 5 :(得分:0)

'/([^_]+)$/'匹配所有下划线 最后一个下划线。

注意:没有必要将下划线与此匹配。这样,当您进行替换时,您不会丢失最后一个下划线。

答案 6 :(得分:0)

这看起来像一个表情符号,但我认为它应该有效:

return preg_replace('/(.*_).*/', $replacement, $string);