Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

split string by spaces and colon but not if inside quotes

having a string like this:

$str = "dateto:'2015-10-07 15:05' xxxx datefrom:'2015-10-09 15:05' yyyy asdf"

the desired result is:

[0] => Array (
    [0] => dateto:'2015-10-07 15:05'
    [1] => xxxx
    [2] => datefrom:'2015-10-09 15:05'
    [3] => yyyy
    [4] => asdf
)

what I get with:

preg_match_all("/\'(?:[^()]|(?R))+\'|'[^']*'|[^(),\s]+/", $str, $m);

is:

[0] => Array (
    [0] => dateto:'2015-10-07
    [1] => 15:05'
    [2] => xxxx
    [3] => datefrom:'2015-10-09
    [4] => 15:05'
    [5] => yyyy
    [6] => asdf
)

Also tried with preg_split("/[\s]+/", $str) but no clue how to escape if value is between quotes. Can anyone show me how and also please explain the regex. Thank you!

like image 851
caramba Avatar asked Oct 09 '15 14:10

caramba


Video Answer


2 Answers

I would use PCRE verb (*SKIP)(*F),

preg_split("~'[^']*'(*SKIP)(*F)|\s+~", $str);

DEMO

like image 74
Avinash Raj Avatar answered Oct 18 '22 06:10

Avinash Raj


Often, when you are looking to split a string, using preg_split isn't the best approach (that seems a little counter intuitive, but that's true most of the time). A more efficient way consists to find all items (with preg_match_all) using a pattern that describes all that is not the delimiter (white-spaces here):

$pattern = <<<'EOD'
~(?=\S)[^'"\s]*(?:'[^']*'[^'"\s]*|"[^"]*"[^'"\s]*)*~
EOD;

if (preg_match_all($pattern, $str, $m))
    $result = $m[0];

pattern details:

~                    # pattern delimiter

(?=\S)               # the lookahead assertion only succeeds if there is a non-
                     # white-space character at the current position.
                     # (This lookahead is useful for two reasons:
                     #    - it allows the regex engine to quickly find the start of
                     #      the next item without to have to test each branch of the
                     #      following alternation at each position in the strings
                     #      until one succeeds.
                     #    - it ensures that there's at least one non-white-space.
                     #      Without it, the pattern may match an empty string.
                     # )

[^'"\s]*          #"'# all that is not a quote or a white-space

(?:                  # eventual quoted parts
    '[^']*' [^'"\s]*  #"# single quotes
  |
    "[^"]*" [^'"\s]*    # double quotes
)*
~

demo

Note that with this a little long pattern, the five items of your example string are found in only 60 steps. You can use this shorter/more simple pattern too:

~(?:[^'"\s]+|'[^']*'|"[^"]*")+~

but it's a little less efficient.

like image 29
Casimir et Hippolyte Avatar answered Oct 18 '22 06:10

Casimir et Hippolyte