4

I'm trying to create common masks from a string like so:

012abc.d+e_fg~hijk => 012{start}.d+{middle}_fg~{end}jk

replace:

$arrFromTo = array(
              'st' => '{pre}',
              'abc' => '{start}',
              'e' => '{middle}',
              'hi' => '{end}',
              'dd' => '{post}'
             );

Instead I keep overlapping replacements and get something like this instead (using a loop of str_replace's):

012{{pre}art}.d+{mi{post}le}_fg~{end}jk

Because the st is found in the already replaced {start} and dd is found in {middle}.

How would you replace the following?

$str = 'abc.d+e_fg~hijk';

echo replace_vars($str); // Desired output: 012{start}.d+{middle}_fg~{end}kJ
5
  • I was thinking rearrange the array so 'dd' => '{post}' was in a different position, but that raised other issues. Commented Oct 28, 2014 at 20:28
  • It's not often I suggest regex, but in this case it might be prudent so you perform lookahead/behinds to check for what you are expecting before you replace Commented Oct 28, 2014 at 20:34
  • @scrowler Unfortunately there's a bug in my PCRE library with variable length negative look behinds. I'm unable to upgrade PCRE library so I stopped going down that path at this point. Commented Oct 28, 2014 at 20:48
  • 1
    The issue you'll have with regular expressions is that php doesn't support variable-length negative lookbehinds. If it did, you could use a regular expression like: /(?<!{\w*)(e)(?!\w*})/i This would mean give me any "e" where it is not between curly braces and any other word character. The \w* makes it a variable-length lookahead/behind. Apparently pcre handles the variable-length lookahead well, but not the lookbehind. Commented Oct 28, 2014 at 20:50
  • You could use a state machine. As you loop through the string, keep a list of the last n characters that match the first n characters of something in your replace list. Once you have a full match, replace the last n characters with your replacement string. Commented Oct 28, 2014 at 21:09

3 Answers 3

6

I might misunderstand, but you don't seem to need regex for the replacing. They're simple, literal replacements.

$from = '012abc.d+e_fg~hijk';
$arrFromTo = array(
              'st' => '{pre}',
              'abc' => '{start}',
              'e' => '{middle}',
              'hi' => '{end}',
              'dd' => '{post}'
             );
$to = strtr($from, $arrFromTo); // 012{start}.d+{middle}_fg~{end}jk

strtr() is awesome. It takes a very readable input and it doesn't re-replace like your problem in the loop.

Sign up to request clarification or add additional context in comments.

Comments

2

You can use preg_replace like this:

$str = '012abc.d+e_fg~hijk';
$arrFromTo = array(
              'st' => '{pre}',
              'abc' => '{start}',
              'e' => '{middle}',
              'hi' => '{end}',
              'dd' => '{post}'
             );

$reArr=array();
foreach($arrFromTo as $k=>$v){
   $reArr['/' . $k . '(?![^{}]*})/'] = $v;
}

echo preg_replace(array_keys($reArr), array_values($reArr), $str);
//=> 012{start}.d+{middle}_fg~{end}jk

Core of this regex is this negative lookaead: (?![^{}]*})

Which avoid matching keys of array if it is enclosed in {...} since all the replacements are enclosed in {...}.

7 Comments

Why preg_replace? Why not str_replace or strtr? No regexing necessary, is there?
How can you use a lookahead in str_replace?
Why do you need lookahead?? They're literal replacements... There are no { and } in the source string.
Oh, okay, so str_replace isn't as smart as strtr. Too bad. Now we know!
Yes right but still you were quite right about strtr +1 to your answer.
|
0

This will search the string for each replacement in order. If it finds one, it will split the string, and search the remainder of the string for any other replacements.

$str = '012abc.d+e_fg~hijk';

$rep = array(
    'st' => '{pre}',
    'abc' => '{start}',
    'e' => '{middle}',
    'hi' => '{end}',
    'dd' => '{post}'
);

$searched = '';

foreach ($rep as $key => $r) {
    if (strpos($str, $key) !== false) {

        $searched .= substr($str, 0, strpos($str, $key)) . $r;
        $str = substr($str, strpos($str, $key) + strlen($key));

    }
}

$searched .= $str;

echo $searched; //012{start}.d+{middle}_fg~{end}jk

It will search and find them in the order that you have specified.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.