-2

I have a problem similar to this question, however with one more twist.

I want to explode the following string:

title:"tab system" color:="blue" price:>10

into

array("title:\"tab system\"", "color:=\"blue\"", "price:>10")

Here's what I've tried so far from the above link:

$text = "title:\"tab system\" color:=\"blue\" price:>10";
preg_match_all('/"(?:\\\\.|[^\\\\"])*"|\S+/', $text, $matches);
print_r($matches);

Which produces:

(
    [0] => title:"tab
    [1] => system"
    [2] => color:="blue"
    [3] => price:>10
)

and:

print_r(str_getcsv($text, ' '));

which produces the same thing.

These solutions don't work for me because as you can see, it's possible that the quotes may not start next to the delimiter (in this case, a space). Also, that's just one example of an input string, there could be many variations of it.

ShaneOH
  • 1,454
  • 1
  • 17
  • 29
  • You could loop over each sign in the string. If you find a quote before a space, do nothing. If the quote is closed, and a space appears, cut out the pice of the string. – DaBra Jul 27 '18 at 07:54
  • 1
    @WiktorStribiżew sure thing, I've edited my question to include this – ShaneOH Jul 27 '18 at 08:54

2 Answers2

2

You may use

preg_split('~(?<!\\\\)(?:\\\\{2})*"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"(*SKIP)(*F)|\s+~s', $s)

See the regex demo

Details

  • (?<!\\) - no \ allowed immediately to the left of the current location
  • (?:\\{2})* - zero or more double backslashes
  • " - a quote
  • [^"\\]* - 0+ chars other than " and \
  • (?:\\.[^"\\]*)* - 0+ sequences of
    • \\. - any escape sequence
    • [^"\\]* - 0+ chars other than " and \
  • " - a quote
  • (*SKIP)(*F) - skipping the match and proceeding to the next match from the current match end location
  • | - or
  • \s+ - 1+ whitespaces in any other contexts.

See the PHP demo:

$s = 'title:"tab system" color:="blue" price:>10';
print_r(preg_split('~(?<!\\\\)(?:\\\\{2})*"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"(*SKIP)(*F)|\s+~s', $s));

Output:

Array
(
    [0] => title:"tab system"
    [1] => color:="blue"
    [2] => price:>10
)
Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
  • 1
    Excellent! Pretty intense regex, it seems to serve the purpose well. Seems that I can accept this answer -- thanks! – ShaneOH Jul 27 '18 at 08:55
0

Look for a whitespace with a double quote either before or after and then split on it:

$result = preg_split( "/((?<=\")\s)|(\s(?=\"))/" , $string );

((?<=\")\s) looks for "[space] but does not select the "

(\s(?=\")) looks for [space]" but does not select the "

The result:

Array
(
    [0] => title:"tab system"
    [1] => color:="blue"
    [2] => price:>10
)
Michel
  • 4,076
  • 4
  • 34
  • 52
  • You need to use regex delimiters. Also, it will fail in a lot of cases, e.g. [`title:"tab \" system" color:="blue" price:>10`](https://regex101.com/r/lHfCTl/1) – Wiktor Stribiżew Jul 27 '18 at 09:15
  • @WiktorStribiżew: Forgot the delimiters, corrected. OP's example has no escaped quotes, so didn't take them into account. – Michel Jul 27 '18 at 09:33
  • I think OP would not have tried a regex that supports escaped sequences in the first place if there was no such requirement. – Wiktor Stribiżew Jul 27 '18 at 09:35