如果是X,如何交替检查字符并将其替换为Y


How to check characters alternatively and replace it with Y if it is X?

我有一个字符串,类似于以下内容:

$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

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

1  => i
4  => i
8  => [space]
12 => t
16 => r
20 => .

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

所以,我想要这个输出:

it is a test stming.

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

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

然后将所有CCD_ 15相互组合。


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

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

有什么解决办法吗?我喜欢用REGEX来做,有可能吗?

只需使用一个带有回调的简单正则表达式(如果utf-8,则添加u标志,.s与换行符匹配)。

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

请在tio.run上查看此演示>it is a test stming.

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

用于匿名函数PHP>=5.3。以下是解决方法(演示)。


更新:@Mariano在他非常好的回答中证明了这是,即使使用单个正则表达式替换也是可能的。感谢您提供的基准测试显示preg_replace_callback解决方案的性能相当糟糕。没有回调的更高效的变体(但仍然有两种模式)。

$str = preg_replace(['/^r/', '/(?:...[^r])*...'Kr/'], 'm', $str);

我还将@revo 2017年的答案包含在Mariano的基准测试中,并在tio.run上运行(100k个循环)。随着更新的PHP和PCRE2,数字似乎略有变化;无正则表达式";tio.run.的线索


.NET或现代浏览器JS regex中,也可以通过可变长度的查找来完成。

如果字符串中的所有字符都是单字节的,则可以使用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);

3v4l演示

输出it is a test stming.


编辑:

正如@Don'tPanic所指出的,使用[]运算符可以更改String,因此不使用

$str=substr_replace($str,$x,$i,1);

你可以直接使用

$str[$i]=$x;

这是使用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.

视频演示


Regex:

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

不过,我必须说,regex对于这项任务来说是一种过度使用。这段代码违反直觉,是一个典型的正则表达式,很难理解/调试/维护。


编辑。后来讨论了性能与代码可读性,所以我做了一个比较基准:

  1. RegEx带有回调(@bobblebubble的回答)
  2. 一个数组中有2个替换项的RegEx(@bobblebubble在评论中的建议)
  3. 没有substr_replace的RegEx(@路人的回答)
  4. 纯RegEx(这个答案)

结果:

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

ideone.com中的基准

试试这个

$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);

这段代码可以帮助您:

// Define variables
$string = "it is a test string.";
$y = 'r';
$x = 'm';
// Convert string to array
$chars = explode('', $string);
// Loop through all characters
foreach ($chars as $key => $char) {
    // Array keys start at 0, so we add 1
    $keyCount = $key+1;
    // Check if deviding the key by 4 doesn't have rest value
    // This means it is devisable by 4
    if ($keyCount % 4 == 0 && $value == $y) {
        $chars[$key] = $x;
    }
}
// Convert back to string
$string = implode($chars);

这里还有另一种方法,使用字符串访问和按字符修改。(因此,它只适用于单字节编码的字符串。)

// First character handled outside the loop because its index doesn't match the pattern
if ($str[0] == $y) $str[0] = $x;
// access every fourth character
for ($i=3; isset($str[$i]) ; $i+=4) {
    // change it if it needs to be changed
    if ($str[$i] == $y) $str[$i] = $x;
}

这将修改原始字符串,而不是创建新字符串,因此如果不应该这样做,则应该在副本上使用它。

派对迟到,抛开'G锚,我会选择(*SKIP)(*F)方法:

$str = "it is a test string.";
echo preg_replace(['~'Ar~', '~.{3}'K(?>r|.(*SKIP)(?!))~'], 'm', $str);

短而干净。

PHP实时演示