I have a string, something like this:
$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
Now I need to check all characters that are multiples of 4 (plus first character). like these:
1 => i
4 => i
8 => [space]
12 => t
16 => r
20 => .
Now, I need to compare them with Y
(Y
is a variable (symbol), for example Y = 'r'
in here). So I want to replace Y
with X
(X
is a variable (symbol) too, for example X = 'm'
in here).
So, I want this output:
it is a test stming.
Here is my solution: I can do that using some PHP function:
strlen($str)
: to count the number of characters (named $sum
)
$sum / 4
: To find characters that are multiples of 4substr($str, 4,1)
: to select specific character (named $char
) {the problem is here}
if ($char == 'r') {}
: to comparestr_replace('r','m',$char)
: to replaceAnd then combining all $char
to each other.
But my solution has two problem:
substr()
does not count [space]
character (As I mentioned above)
Well, is there any solution? I like to do that using REGEX, Is it possible?
This is an alternative using 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.
ideone demo
Regex:
\G(?:^|(?(?<!^.).)..(?:.{4})*?)\Km
\G
is an assertion to the end of last match (or start of string)(?:^|(?(?<!^.).)..(?:.{4})*?)
matches:
^
start of string, to check at position 1(?(?<!^.).)
is an if clause that yields:
..(?:.{4})*?)
2 chars + a multiple of 4 if it has just replaced at position 1...(?:.{4})*?)
3 chars + a multiple of 4 for successive matches\K
resets the text matched to avoid using backreferencesI must say though, regex is an overkill for this task. This code is counterintuitive and a typical regex that proves difficult to understand/debug/maintain.
EDIT. There was a later discussion about performance vs. code readability, so I did a benchmark to compare:
substr_replace
(@Passerby's answer).Result:
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
Benchmark in ideone.com
Late to the party, puting aside \G
anchor, I'd go with (*SKIP)(*F)
method:
$str = "it is a test string.";
echo preg_replace(['~\Ar~', '~.{3}\K(?>r|.(*SKIP)(?!))~'], 'm', $str);
Short and clean.
PHP live demo
Could just use a simple regex with callback (add u
modifier if utf-8, s
for .
to match newline).
$str = preg_replace_callback(['/^./', '/.{3}\K./'], function ($m) {
return $m[0] == "r" ? "m" : $m[0];
}, $str); echo $str;
it is a test stming.
^.
any first character\K
resets after .{3}
any three characters, only want to check the fourth .
See demo at eval.in
For use with anonymous function PHP >= 5.3 is required. Here's the workaround.
function cb($m) { return $m[0] == "r" ? "m" : $m[0]; }
$str = preg_replace_callback(['/^./', '/.{3}\K./'], 'cb', $str);
Another demo at eval.in
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With