3

我可以使用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!!!
4

7 回答 7

3

你很接近,你正在寻找的模式是这样的:

([^_]*)$

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

于 2012-04-19T20:16:34.433 回答
2

根据您的常用搜索字符串和您的 PCRE 版本,preg_replace或者strrpos可能会最好:

职能

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_replacestrrpos大量下划线为止。

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

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
于 2012-04-19T20:53:17.703 回答
1

问题是第一个下划线匹配。除了下划线之外,你想要下划线后面的任何内容:

'/_([^_]*)$/'
于 2012-04-19T20:17:07.860 回答
1

正则表达式/_(.*)$/匹配下划线,后跟任何文本,然后是字符串的结尾。没有什么可以阻止“任何文本”包含下划线,默认情况下,匹配器将选择最左边、最长的匹配项。所以在“a_b_c”中,它在“a”之后匹配。

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

此外,由于您没有对捕获的组做任何事情,因此不需要括号。而且,根据您的示例,由于您不想替换下划线本身,因此您应该将其排除在正则表达式之外。

function bar($string, $replacement)
{
    return preg_replace('/[^_]*$/', $replacement, $string, 1);
}
于 2012-04-19T20:17:53.240 回答
1

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

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

于 2012-04-19T20:22:42.807 回答
0

'/([^_]+)$/'匹配除最后一个下划线之后的下划线以外的所有内容。

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

于 2012-04-19T20:16:21.440 回答
0

这看起来像一个表情符号,但我认为它应该可以工作:

return preg_replace('/(.*_).*/', $replacement, $string);
于 2012-04-19T22:23:57.887 回答