Firstly, I'm not new to programming, but I am new to PHP, so please forgive me if the answer to this is screamingly obvious...I've read through the manual, but haven't found anything. I've got a big string full of name/value pairs like so, where the delimiter is actually the null character "\x00"

$data = "variant.standard.password.0.timelimit.25.options..hasoverrides.0.dedicated.1.linux.0";

Now, I need to break this up into an associative array like so:

$assoc = array(
  'variant'      => 'standard',
  'password'     => '0',
  'timelimit'    => '25',
  'options'      => '',
  'hasoverrides' => '0',
  'dedicated'    => '1',
  'linux'        => '0',
);

I could do this by exploding the entire string into a normal array, then looping through and assigning every two values to a different array, one as the key and the other as a value, but I can't help but think there has to be a better way that doesn't require duplicating the entire structure.

Normally I wouldn't sweat it, but the structure could potentially become MUCH larger than the 7 pairs in this example, causing a massive performance hit if we duplicate the whole thing every time. I'd appreciate any advice anyone can provide...thanks!

    This is all I could come up with that doesn't involve an interim array. I make no claims as to its performance compared to using such an interim array:

    <?php
    $data = "variant.standard.password.0.timelimit.25.options..hasoverrides.0.dedicated.1.linux.0";
    $data = urlencode($data);
    $data = preg_replace('/([^.]*)\.([^.]*)\./', "\\1=\\2&", $data);
    $data = str_replace('.', '=', $data);
    parse_str($data, $dataArray);
    // show result:
    printf("<pre>%s</pre>", print_r($dataArray, TRUE));
    ?>
    

    Output:

    Array
    (
        [variant] => standard
        [password] => 0
        [timelimit] => 25
        [options] => 
        [hasoverrides] => 0
        [dedicated] => 1
        [linux] => 0
    )
    

      Hrr-rm. Where is this data coming from and couldn't you not get it into a friendlier form before having to deal with it?

      I think NogDog's turning it into a querystring is quite clever - altering the delimiters in place is obvious, but choosing those delimiters to result in a querystring gets around the hassles of parsing. One possible problem, though, that each of those "$data=" lines can require twice as much memory as $data alone (after the evaluation of the right-hand side, but before the assignment). If that is an issue though, then it really would be better to parse the data as you're reading it in from outside, instead of reading it all and then parsing it.

      But here's another crack at it using [man]strtok/man (which I usually don't use, so watch that).

      $tok = strtok($data, " \x00");
      $dataArray = array();
      while ($tok !== false) {
          $dataArray[$tok]=strtok($data, "\x00");
          $tok = strtok($data, "\x00");
      }
      

        You could even use the good ole fashion C style for() loop

        $parts = explode("\x00", $data);
        $dataArray = array();
        for ($i=0, $n=sizeof($parts); $i<$n; $i++) {
        	$dataArray[$parts[$i]] = $parts[++$i];
        }
          Weedpacket wrote:

          Hrr-rm. Where is this data coming from and couldn't you not get it into a friendlier form before having to deal with it?

          I think NogDog's turning it into a querystring is quite clever - altering the delimiters in place is obvious, but choosing those delimiters to result in a querystring gets around the hassles of parsing. One possible problem, though, that each of those "$data=" lines can require twice as much memory as $data alone (after the evaluation of the right-hand side, but before the assignment). If that is an issue though, then it really would be better to parse the data as you're reading it in from outside, instead of reading it all and then parsing it.

          But here's another crack at it using [man]strtok/man (which I usually don't use, so watch that).

          $tok = strtok($data, " \x00");
          $dataArray = array();
          while ($tok !== false) {
              $dataArray[$tok]=strtok($data, "\x00");
              $tok = strtok($data, "\x00");
          }
          

          My first thought was to use strtok() with '.' as the token, but I couldn't figure out how to get around the instance of an empty value ('..').

            dream.scape wrote:

            You could even use the good ole fashion C style for() loop

            Yeah, but the poster specifically wanted to avoid exploding. (Don't we all? 😉) On the other hand, the whole of the rest of this post is predicated on the assumption that the original poster really has determined that explode() is unsuitable for some reason.

            NogDog wrote:

            My first thought was to use strtok() with '.' as the token, but I couldn't figure out how to get around the instance of an empty value ('..').

            Um, yeah; that changed in 4.1, didn't it (I botched the code anyway, by reinitialising the string every time: variantvariantvariantvariant.....).

            Unless"MUCH larger" means "on the order of several megabytes" I'd go with your code (though I still wonder how much more memory would be required for a $data=explode("\x00", $data)). If that is the size of the data, then I'd parse it while I'm reading it ([man]stream_get_line[/man]) instead of leaving that until after. (And if this code is statically declared in the code, then it would be much better written as an associative array to begin with!)

            Actually, I could use stream_get_line on a variable.... lessee... I'm too lazy to write the stream wrapper myself so I'll just whack in the VariableStream class that appears on the [man]stream_wrapper_register[/man] page:

            
            stream_wrapper_register("var", "VariableStream")
               or die("Failed to register protocol");
            
            $dataArray = array();
            $fp = fopen("var://data", "r");
            while(!feof($fp))
            {
            	$name = stream_get_line($fp, 128, "\x00");
            	$value = stream_get_line($fp, 128, "\x00");
            	$dataArray[$name]=$value;
            }
            print_r($dataArray);
            

            Um, yeah, that works. But if this is coming from a file then using stream_get_line() directly on the file handle makes far more sense.

            And after all that, if this data doesn't change, use var_dump to make a PHP-parseable file that contains the associative array.

            Oh, one last little giggle. Does anyone else feel like having a little ... giggle?

            $data.="\x00";
            $dataArray = array();
            preg_replace('/([a-z0-9]+)\x00([a-z0-9]+)\x00/ei', '$dataArray["$1"]="$2";', $data);
            print_r($dataArray);
            
              6 days later

              to all that offered their advice, thanks a bunch! i ended up using an amalgamation of your suggestions. i liked the format of the strtok() idea, except that the skipping an empty value screws things up in my case, so i used an explode() limited to 2 elements:

              // initial split to grab first key
              list($str,$data) = explode("\x00", $data, 2);
              
              // loop until nothing left in data string
              while ($data != '') {
                // assign next element as value, then get next key
                list($assoc[$str],$data) = explode("\x00", $data, 2);
                list($str,$data) = explode("\x00", $data, 2);
              }

              it looks a bit messy, but it works well and its scalable. thanks again for the help, all!

                Write a Reply...