uniqu3
uniqu3

Reputation: 3

PHP regex to match key value pairs from a given string

i hope someone can help.

I have a string as following

$string = 'latitude=46.6781471,longitude=13.9709534,options=[units=auto,lang=de,exclude=[hourly,minutely]]';

Now what i am trying is to create an array out of each key, value pair but badly failing with regex for preg_match_all()

Currently my attempts aren't giving desired results, creating key => value pairs works as long as there are no brackets, but i have absolutely no idea how to achieve a multidimensional array if key contains key/value pairs inside brackets in example.

Array (
[0] => Array
    (
        [0] => latitude=46.6781471,
        [1] => longitude=13.9709534,
        [2] => options=[units=si,
        [3] => lang=de,
    )

[1] => Array
    (
        [0] => latitude
        [1] => longitude
        [2] => options=[units
        [3] => lang
    )

.. and so on

Where in the end i would like to achieve results as following.

Array (
[latitude] => 46.6781471
[longitude] => 13.9709534
[options] => Array
    (
        [units] => auto
        [exclude] => hourly,minutely
    )
)

I would appreciate any help or example how i can achieve this from a given string.

Upvotes: 0

Views: 623

Answers (2)

Dawid Loranc
Dawid Loranc

Reputation: 892

If you don't want parser you can also try this code. It converts your string to JSON and decode to array. But as others said, I think you should try the approach with JSON. If you're sending this string by XmlHttpRequest in JavaScript it will not be hard to create proper JSON code to send.

$string = 'latitude=46.6781471,longitude=13.9709534,options=[units=auto,lang=de,exclude=[hourly,minutely]]';

$string = preg_replace('/([^=,\[\]\s]+)/', '"$1"', $string);

$string = '{' . $string . '}';
$string = str_replace('=', ':', $string);
$string = str_replace('[', '{', $string);
$string = str_replace(']', '}', $string);

$string = preg_replace('/({[^:}]*})/', '|$1|', $string);
$string = str_replace('|{', '[', $string);
$string = str_replace('}|', ']', $string);

$result = json_decode($string, true);

print_r($result);

Upvotes: 0

Waldson Patricio
Waldson Patricio

Reputation: 1529

Regular expression isn't the right tool to deal with recursive matches. You can write a parser instead of a regex (or use JSON, query string, XML or any other commonly used format):

function parseOptionsString($string) {

    $length        = strlen($string);
    $key           = null;
    $contextStack  = array();
    $options       = array();

    $specialTokens = array('[', ']', '=', ',');
    $buffer     = '';

    $currentOptions = $options;

    for ($i = 0; $i < $length; $i++) {
        $currentChar = $string[$i];

        if (!in_array($currentChar, $specialTokens)) {
            $buffer .= $currentChar;
            continue;
        }

        if ($currentChar == '[') {
            array_push($contextStack, [$key, $currentOptions]);
            $currentOptions[$key] = array();
            $currentOptions       = $currentOptions[$key];
            $key                  = '';
            $buffer               = '';
            continue;
        }

        if ($currentChar == ']') {
            if (!empty($buffer)) {
                if (!empty($key)) {
                    $currentOptions[$key] = $buffer;    
                } else {
                    $currentOptions[] = $buffer;
                }
            }


            $contextInfo     = array_pop($contextStack);
            $previousContext = $contextInfo[1];
            $thisKey         = $contextInfo[0];

            $previousContext[$thisKey] = $currentOptions;

            $currentOptions        = $previousContext;
            $buffer                = '';
            $key                   = '';
            continue;
        }

        if ($currentChar == '=') {
            $key    = $buffer;
            $buffer = '';
            continue;
        }

        if ($currentChar == ',') {

            if (!empty($key)) {
                $currentOptions[$key] = $buffer; 
            } else if (!empty($buffer)) {
                $currentOptions[] = $buffer;
            }
            $buffer        = '';
            $key           = '';
            continue;
        }

    }

    if (!empty($key)) {
        $currentOptions[$key] = $buffer;
    }

    return $currentOptions;
} 

this gives the following output:

print_r(parseOptionsString($string));

Array
(
    [latitude] => 46.6781471
    [longitude] => 13.9709534
    [options] => Array
        (
            [units] => auto
            [lang] => de
            [exclude] => Array
                (
                    [0] => hourly
                    [1] => minutely
                )

        )

)

Note also that you want a special syntax for arrays with only comma separated values (exclude=[hourly,minutely] becomes exclude => hourly,minutely and not exclude => array(hourly, minutely)). I think this is an inconsistency in your format and I wrote the parser with the "correct" version in mind.

Upvotes: 2

Related Questions