How to check characters as an alternative and replace it with Y if it is X?

I have a line, something like this:

$str ="it is a test string."; // for more clarification itisateststring . 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 the first character). eg:

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

Now I need to compare them with Y ( Y is a variable (character), for example Y = 'r' here). So I want to replace Y with X ( X also a variable (character), for example X = 'm' ).

So I need this output:

 it is a test stming. 

Here is my solution : I can do this with some PHP function:

  • strlen($str) : count the number of characters (named $sum )
  • $sum / 4 : To find characters that are multiples of 4
  • substr($str, 4,1) : to select a specific character (named $char ) {problem )
  • if ($char == 'r') {} : compare
  • str_replace('r','m',$char) : replace

And then combining all $char with each other.


But my solution has two problems :

  • substr() not considered a [space] character (as I mentioned above) Combination of characters
  • a bit complicated. (This is necessary for waste treatment).

Ok, is there any solution? I like to do this with REGEX, is this possible?

+8
string php regex
source share
7 answers

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 the statement at the end of the last match (or the beginning of the line)
  • (?:^|(?(?<!^.).)..(?:.{4})*?) Matches:
    • ^ start of line to check at position 1
    • (?(?<!^.).) is the if condition, which gives:
      • ..(?:.{4})*?) 2 characters + a multiple of 4 if it has just been replaced at position 1
      • ...(?:.{4})*?) 3 characters + a multiple of 4 for consecutive matches
  • \K discards text to match to avoid using backreferences

I have to say that regex is redundant for this task. This code is a non-intuitive and typical regular expression that is hard to understand / debug / maintain.


EDIT . There was a later discussion about code performance and readability, so for comparison, I did a comparative test:

  • RegEx with callback ( @bobblebubble answer ).
  • RegEx with 2 replacements in an array ( @bobblebubble sentence in a comment ).
  • No RegEx with substr_replace ( @Passerby answer ).
  • Pure RegEx (this 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 at ideone.com

+4
source share

You can simply use a simple regular expression with callback (add u modifier if utf-8, s for . To match a new line).

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

This is a test test.

  • 1st pattern: ^. any first character
  • 2nd pattern: \K discards after .{3} any three characters, only wants to check the fourth .

See demo at eval.in


For use with an anonymous function, PHP> = 5.3 is required. Here is a workaround.

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

Another demo on eval.in

+8
source share

If all characters in your string are in one byte, you can use something from the official link to the official PHP language :

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

demo 3v4l

Outputs it is a test stming.


Edit:

As @ Don'tPanic points out, String can be changed using the [] operator, so instead of using

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

you can just use

 $str[$i]=$x; 
+5
source share

try it

 $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); 
+4
source share

This piece of code can help you on your way:

 // 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); 
+1
source share

Here's another way to do this using string access and character modification . (Therefore, this is only useful for single-byte encoded strings.)

 // 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; } 

This modifies the original line, rather than creating a new line, so if this does not happen, it should be used in a copy.

+1
source share

Late to the side, laying anchor \G , I would go with the method (*SKIP)(*F) :

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

Short and clean.

Demo for PHP

+1
source share

All Articles