1

How can I format an arbitrary string according to a flexible pattern? The only solution I came up with is using regular expressions, but then I need 2 "patterns" (one for the search and one for the output).

Example:

$str = '123ABC5678"; 

Desired output: 12.3AB-C5-67.8

I would like to use a pattern in a variable (one that a user can easily define without knowledge of regular expressions) It could look like this:

$pattern = '%%.%%%-%%-%%.%';

So the user would just have to use 2 different characters (% and .)

A solution with regex would look like this:

$str = '123ABC5678';
$pattern_src = '@(.{2})(.{3})(.{2})(.{2})(.{1})@';
$pattern_rpl = "$1.$2-$3-$4.$5";

$res = preg_replace($pattern_src, $pattern_rpl, $str);
//$res eq 12.3AB-C5-67.8

Way too complicated since the user would need to define $pattern_src and $pattern_rpl. If the string could vary in length, it would be even more complex to explain.

Yes, I could write a function/parser that builds the required regular expressions based on a simple user pattern like %%.%%%-%%-%%.%. But I wonder if there is any "built in" way to achieve this with php? I was thinking about sprintf etc., but that doesn't seem to do the trick. Any ideas?

0

4 Answers 4

7

I was thinking about sprintf etc., but that doesn't seem to do the trick.

You're on the right track. You can accomplish this with vsprintf as follows:

$str = '123ABC5678';
$pattern = '%%.%%%-%%-%%.%';
echo vsprintf(str_replace('%', '%s', $pattern), str_split($str));

Output:

12.3AB-C5-67.8

This is assuming the number of % characters in $pattern match the length of $str.

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

1 Comment

This is the shortest and fastest solution. It's twice as fast as a for-loop and 3 times as fast as the preg_match_all+foreach approach (tested with 100000 iterations).
3

Why not write a simple parser that works as follows:

For each character of pattern:

  • if you match percent character, output next character from input
  • if you match any other character, output it

$str = '123ABC5678';
$pattern = '%%.%%%-%%-%%.%';

if (strlen($str) < substr_count($pattern, '%'))
  Die('The length of input string is lower than number number of placeholders');
$len = strlen($pattern);
$stringIndex = 0;
$output = '';
for ($i = 0; $i < $len; $i++) {
  if ($pattern[$i] === '%') {
    $output .= $str[$stringIndex];
    $stringIndex++;
  } else {
    $output .= $pattern[$i];
  }
}
echo $output;

Comments

2

I have a similar solution that looks like this.

<?php
$format = '%%.%%%-%%-%%.%';
$string = '123ABC5678';

$new_string = '';
$c = 0;
for( $i = 0; $i < strlen( $format ); $i++ )
{
  if( $format[ $i ] == '%' )
  {
    $new_string .= $string[ $c ];
    $c++;
  }
  else
  {
    $new_string .= $format[ $i ];
  }
}

echo $new_string;

Output:

12.3AB-C5-67.8

Comments

2

How about this pattern from the user?

2.3-2-2.1
Where the pattern is a number means n chars, a dot or dash means add a dot or dash.

Now you make a regex to parse the user input:

preg_match_all("/(.)/", $User_input, $pattern);

Now you will have an array with either numbers or dots and dashes.

So loop through the array and build the string:

$string = '123ABC5678';
$User_input = "2.3-2-2.1";

preg_match_all("/(.)/", $User_input, $pattern);

$i=0;
$str="";
foreach($pattern[1] as $val){

    if(is_numeric($val)){
        $str .= substr($string,$i,$val);
        $i=$i+$val;
    }else{
        $str .= $val;
    }

}


 echo $str;

https://3v4l.org/5eg5G

4 Comments

OP did say "without regex"
@mistermartin he said (as far as I understand) no end user regex. He wanted a way that was simple for the user to use. The question is even taged regex
you're right. Even though the title says without regex, OP may just be looking for a simpler solution (that may still have regex). Have an upvote for a working solution :)
Thanks! But the code can work just as good without regex now that I think about it

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.