0

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.

1
  • Just curious, but why would you create and try to parse a completely new serializing format? Why not just use JSON or similar standard that already has a complete library for parsing and getting the correct values out. The only way to make this work with a regex would be to make it a recursive regex that can match the square brackets which isn't exactly easy. You would almost be better looping over each character until you hit a special character (comma, equals or square bracket) and working with that. Commented Oct 20, 2016 at 16:59

2 Answers 2

2

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.

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

2 Comments

Thanks, this gives me desired output, but probably both of you are right and i should possibly try a different approach instead of over complicating things, i guess i tend at complicating thing.
Yeah, you should definitely choose another approach. Keep things simple is the rule above us all.
0

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);

Comments

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.