0

I have a string with color names, separated by commas or | characters:

$s = 'Blue | Red (light, dark) | Green, Yellow, Brown';

I need to convert it to array:

$colors = preg_split('![\|,]!', $s);

But the result is:

Array
(
    [0] => Blue 
    [1] =>  Red (light
    [2] =>  dark) 
    [3] =>  Green
    [4] =>  Yellow
    [5] =>  Brown
)

It splits the "light, dark" string with comma. I'd like to split with commas not inside parentheses. Desired result:

Array
(
    [0] => Blue 
    [1] =>  Red (light, dark) 
    [2] =>  Green
    [3] =>  Yellow
    [4] =>  Brown
)

How can I solve it?

Lay András
  • 795
  • 2
  • 9
  • 14

3 Answers3

2
$str = 'Blue | Red (light, dark) | Green, Yellow, Brown (light | bright)';
$res = preg_split('/
                     (          # group 1
                       [^|,]*   # 0 or more not pipe or comma
                       \(       # open parenthesis
                       [^()]*   # 0 or more not parenthesis
                       \)       # close parenthesis
                     )          # end group 1
                    |           # OR
                     [|,]       # a pipe or a comma
                   /x', 
                   $str, -1,
                    PREG_SPLIT_NO_EMPTY
                  | PREG_SPLIT_DELIM_CAPTURE);
print_r($res);

Output:

Array
(
    [0] => Blue 
    [1] =>  Red (light, dark)
    [2] =>  
    [3] =>  Green
    [4] =>  Yellow
    [5] =>  Brown (light | bright)
)
Toto
  • 89,455
  • 62
  • 89
  • 125
1

You need to mask the parenthesed portions of your input string before the replacement:

$s = 'Blue | Red (light, dark) | Green, Yellow, Brown';

// Mask the parenthesed sections.
// Replace the ',' character with a unique string which you can guarantee will not occur (here: '===')
$sr =
    preg_replace_callback(
       '!\([^\)]+\)!'
     , function ( $p_hit ) { return str_replace(',', '===', $p_hit[0]); }
     , $s
    );

// original split
$colors = preg_split('![\|,]!', $sr);

// undo masking 
$colors = array_map (
    function ($elem) { return str_replace('===', ',', $elem); }
  , $colors
);

// Print result
print_r ($colors);

Output:

Array
(
[0] => Blue 
[1] => Red (light, dark) 
[2] => Green
[3] => Yellow
[4] => Brown
)

Note

This solution does not work for source string containing nested parentheses.

collapsar
  • 17,010
  • 4
  • 35
  • 61
0

You only need to disqualify delimiters inside of the parentheticals with (*SKIP)(*FAIL). I've also added zero or more spaces before and after the delimiter so that the output values are trimmed.

Code: (Demo)

$s = 'Blue | Red (light, dark) | Green, Yellow, Brown';
$colors = preg_split('/\([^)]+(*SKIP)(*FAIL)|\s*[,|]\s*/',$s);
var_export($colors);

echo "\n";

$str = 'Blue | Red (light, dark) | Green, Yellow, Brown (light | bright)';
$colors = preg_split('/\([^)]+(*SKIP)(*FAIL)|\s*[,|]\s*/',$str);
var_export($colors);

Output:

array (
  0 => 'Blue',
  1 => 'Red (light, dark)',
  2 => 'Green',
  3 => 'Yellow',
  4 => 'Brown',
)
array (
  0 => 'Blue',
  1 => 'Red (light, dark)',
  2 => 'Green',
  3 => 'Yellow',
  4 => 'Brown (light | bright)',
)

For arguement's sake, you could just use this on your sample input string:

var_export(preg_match_all('/[a-z]+(?: \([^)]+\))?/i',$str,$out)?$out[0]:'fail');

This case-insensitively matches the colors, then optionally includes a trailing parenthetical expression. Same output.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
  • @LayAndrás I have posted an efficient / refined solution that outputs trimmed strings. I feel this is the method that you should be using in your project. Do you have any questions about my answer? – mickmackusa Dec 18 '17 at 21:36