我有一个字符串,类似于以下内容:
$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
替换Y
(X
也是一个变量(符号),例如这里的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相互组合。
但我的解决方案有两个问题:
substr()
不计算[space]
字符(如上所述)- 组合字符有点复杂(需要进行一些废物处理)
有什么解决办法吗?我喜欢用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子句,它产生:..(?:.{4})*?)
2个字符+4的倍数(如果它刚刚在位置1处被替换)- 连续匹配的
...(?:.{4})*?)
3个字符+4的倍数
'K
重置匹配的文本以避免使用反向引用
不过,我必须说,regex对于这项任务来说是一种过度使用。这段代码违反直觉,是一个典型的正则表达式,很难理解/调试/维护。
编辑。后来讨论了性能与代码可读性,所以我做了一个比较基准:
- RegEx带有回调(@bobblebubble的回答)
- 一个数组中有2个替换项的RegEx(@bobblebubble在评论中的建议)
- 没有
substr_replace
的RegEx(@路人的回答) - 纯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实时演示