开发者

PHP:PCRE: How to replace repeatable char

for example I have following string:

a_b__c___d____e

How to preg_replace char _ to char '-', but only if part ' __...' contains more than N repeated _.

I hope you understand me ))

source: a_b__c___d____e
cond: change '_' where 2 or more
result: a_b--c---d--开发者_运维技巧--e

or

source: a_b__c___d____e_____f
cont: change '_' where 4 or more
result: a_b__c___d----e-----f

Thanks!

p.s. Interesting solution without using loops. How implement it with loops (I think) know anybody. Just a one regex and preg_replace.


Here is another one using the e modifier:

 $str = 'a_b__c___d____e_____f';
 echo preg_replace('/_{4,}/e', 'str_repeat("-", strlen("$0"))', $str);

Replace 4 by the number you need. Or as function:

function repl($str, $char, $times) {
    $char = preg_quote($char, '/');
    $times = preg_quote($times, '/');
    $pattern = '/' . $char . '{' . $times . ',}/e',
    return preg_replace($pattern, 'str_repeat("-", strlen("$0"))', $str);
}


$source = 'a_b__c___d____e_____f';
function yourfunc($param)
{
    $count  = strlen($param);
    $return = '';
    for ($i = 0; $i < $count; $i++)
    {
        $return .= '-';
    }
    return $return;
}
echo preg_replace('#(_{4,})#e', 'yourfunc("$1");', $source);

A solution without callback function and loop is much harder to read.

preg_replace('#(_{4,})#e', 'implode("", array_pad(array(), strlen("$1"), "-"));', $source);


this is inline solution :

preg_replace('/(_{2,})/ie', 'str_repeat("-",strlen("$1"));', $source);

and reusable funciton:

$source = 'a_b__c___d____e_____f';


    function replace_repeatable($source,$char,$replacement,$minrepeat = 2)
    {
          return preg_replace('/(' . preg_quote($char) . '{' . $minrepeat . ',})/ie', 'str_repeat("' . $replacement . '",strlen("$1"));', $source);
    }

    $b = replace_repeatable($source,'_','-',4);


As referring to php.net documenation using modifier e is discouraged,

This feature has been DEPRECATED as of PHP 5.5.0. Relying on this feature is highly discouraged.

so we'd better to achieve our goal without using this modifier.


Here's solution based on up to date PHP's tools:

$source = 'a_b__c___d____e';

echo preg_replace_callback( "%(_{2,})%i", function($matches) {return str_repeat( "-", strlen($matches[1]) ); }, $source );
/* in callback function matches[0] is whole matched pattern, groups go like this matches[1],matches[2]... */

Even with e still available in our PHP environment, it is generally better to use callback function - thank's to callback we avoid rather unsafe combination of addslashes() function and string evaluation, since running preg_replace with mentioned modifier engages both actions at a time.


A preg_replace_callback has been available since version 4.0.5, but function($matches) {} is an anonymous function which is actually much newer language feature, to run this code u need PHP in version 5.3.0 or newer.


You can replace the dashes one by one using the \G anchor to ensure a contiguity from the position of the first - (followed by n-1 other -) to the last one. This way you only have to check the number of following dashes after the first one:

echo preg_replace('~\G(?!^)_|_(?=_)~', '-', $str);

demo

for n=2:

\G(?!^)_|_(?=_)

for n=3:

\G(?!^)_|_(?=_{2})

for n=4:

\G(?!^)_|_(?=_{3})

etc.

The first branch \G(?!^)_ succeeds only when there's a successfull match at the previous position. In other words, that means this branch will fail until the next second branch succeeds. The second branch _(?=_{n-1}) is devoted to the first underscore. It checks using a lookahead assertion the number of following underscores.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜