如何检查字符,如果是X则用Y替换?

Sha*_*deh 9 php regex string

我有一个字符串,如下所示:

$str ="it is a test string.";

 // for more clarification

 i t   i s   a   t e  s  t     s  t  r  i  n  g  .
 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Run Code Online (Sandbox Code Playgroud)

现在我需要检查所有4的倍数(加上第一个字符)的字符.像这些:

1  => i
4  => i
8  => [space]
12 => t
16 => r
20 => .
Run Code Online (Sandbox Code Playgroud)

现在,我需要将它们与Y (Y它是变量(符号))Y = 'r'进行比较,例如在这里.所以我想替换Y为X(X也是一个变量(符号),例如X = 'm'在这里).

所以,我想要这个输出:

it is a test stming.
Run Code Online (Sandbox Code Playgroud)

这是我的解决方案:我可以使用一些PHP函数来做到这一点:

  • strlen($str):计算字符数(命名$sum)
  • $sum / 4:查找4的倍数的字符
  • substr($str, 4,1):选择特定字符(命名$char) { 问题在这里}
  • if ($char == 'r') {}: 比较
  • str_replace('r','m',$char): 取代

然后将所有内容$char相互结合.


但我的解决方案有两个问题:

  1. substr()不计算[space]字符(如上所述)
  2. 组合字符有点复杂.(需要一些废物处理)

那么,有什么解决方案吗?我喜欢用REGEX做到这一点,有可能吗?

bob*_*ble 10

可以只使用一个简单的正则表达式与回调(补充u 修改如果UTF-8,s为.配合新行).

$str = preg_replace_callback(['/^./', '/.{3}\K./'], function ($m) {
         return $m[0] == "r" ? "m" : $m[0];
       }, $str); echo $str;
Run Code Online (Sandbox Code Playgroud)

这是一次测试.

  • 第一种模式:^.任何第一个角色
  • 第二种模式:\K在.{3}任意三个字符后重置,只想检查第四个.

请参阅eval.in上的演示


要与匿名函数一起使用,需要PHP> = 5.3.这是解决方法.

function cb($m) { return $m[0] == "r" ? "m" : $m[0]; }
$str = preg_replace_callback(['/^./', '/.{3}\K./'], 'cb', $str);
Run Code Online (Sandbox Code Playgroud)

eval.in上的另一个演示

  • 这是一个很好的答案+1.@Shafizadeh:对不起,我不得不赶去开会.回来之后,我注意到了很好的新答案,因此需要更正. (2认同)
  • @Shafizadeh我也会选择代码,或者任何编辑代码的人,都可以轻松理解/调试/维护代码.回调的唯一缺点是性能.基准:http://ideone.com/5ZFCFe (2认同)

Pas*_*rby 6

如果字符串中的所有字符都是单字节,则可以使用PHP的官方语言参考:

$str ="it is a test string.";
$y="r";
$x="m";
$len=strlen($str);
if($str[0]==$y)
{
    $str=substr_replace($str,$x,0,1);
}
if($len>=3)
{
    for($i=3;$i<$len;$i+=4)
    {
        if($str[$i]==$y)
        {
            $str=substr_replace($str,$x,$i,1);
        }
    }
}
var_dump($str);
Run Code Online (Sandbox Code Playgroud)

3v4l演示

输出 it is a test stming.


编辑:

正如@DissPanic指出的那样,String是可变的使用[]运算符,所以而不是使用

$str=substr_replace($str,$x,$i,1);
Run Code Online (Sandbox Code Playgroud)

你可以使用

$str[$i]=$x;
Run Code Online (Sandbox Code Playgroud)


Dro*_*dow 5

尝试这个

$str ="it is a test string.";
$y="r";
$x="m";    

$splite_array = str_split($str);

foreach ($splite_array as $key => $val)
{
    if($key % 4 == 0 && $val == $y)
    {
        $splite_array[$key] = $x;
    }
}

$yout_new_string  = implode($splite_array);
Run Code Online (Sandbox Code Playgroud)

  • @Shafizadeh [查看此演示](https://eval.in/469422),我也放弃了投票,因为想法很简洁。 (2认同)

Mar*_*ano 5

这是使用preg_replace()的替代方法

$y = 'r';
$y = preg_quote($y, '/');
$x = 'M';
$x = preg_quote($x, '/');
$subject = 'rrrrrr rrrrr rrrrrr rrrr rrrr.';

$regex = "/\\G(?:^|(?(?<!^.).)..(?:.{4})*?)\\K$y/s";

$result = preg_replace($regex, $x, $subject);

echo $result;
// => MrrMrr MrrrM rrMrrr rrrM rrMr.
Run Code Online (Sandbox Code Playgroud)

ideone demo


正则表达式:

\G(?:^|(?(?<!^.).)..(?:.{4})*?)\Km
Run Code Online (Sandbox Code Playgroud)
  • \G 是最后一个匹配结束(或字符串开头)的断言
  • (?:^|(?(?<!^.).)..(?:.{4})*?) 火柴:
    • ^ 字符串的开头,以检查位置1
    • (?(?<!^.).) 是一个if子句,其产生:
      1. ..(?:.{4})*?) 2个字符+ 4的倍数(如果刚在位置1处替换的话)
      2. ...(?:.{4})*?) 3个字符+ 4的倍数以进行连续匹配
  • \K 重置匹配的文本,以避免使用反向引用

不过,我必须说,正则表达式对于此任务来说是一个过大的杀伤力。该代码是违反直觉的,并且是典型的正则表达式,证明难以理解/调试/维护。


编辑。稍后进行了有关性能与代码可读性的讨论,因此我做了一个基准比较:

  1. RegEx带有回调(@bobblebubble的答案)。
  2. RegEx数组中有2个替换项(@bobblebubble在注释中的建议)。
  3. 没有正则表达式substr_replace((@Passerby的答案)。
  4. 纯正则表达式(此答案)。

结果:

Code #1(with_callback):   0.548 secs/50k loops
Code #2(regex_array):     0.158 secs/50k loops
Code #3(no_regex):        0.120 secs/50k loops
Code #4(pure_regex):      0.118 secs/50k loops
Run Code Online (Sandbox Code Playgroud)

Benchmark in ideone.com

  • 另外,“尽管如此,我必须说,正则表达式对于此任务来说是过大的选择。” 我不会说* overkill *,但这将是维护方面的噩梦:我确定@Shafizadeh不想回到SO那里找人来解释正则表达式在做什么。:-) (3认同)