Page 1 of 1

Regular Expressions Insight

Posted: Tue Apr 03, 2007 11:34 pm
by JeFFb68CAM
Hey there,

I'm working on an open source chat software, and it's time I rewrite my commands; I've got this idea I'd like to get accomplished but I'm having a hard time getting it done.

I want my commands do look something like this: (any of you that have linux experience should get what I'm trying to accomplish)

/example -custom="Custom User" -time=65 This is an example message

now, a more real world example would be:

/ban -time=15 Bad_User Stop Flooding

As you can see, I am going for the linux cmd line goal, this is easy but I am struggling with trying to get the values inside the quotes. This is the code I have so far.

Code: Select all

$cmd = "-duration=55 -perm=yes -rofl=5 -nice=\"this is a tesT\" JeFFb68CAM test message";

preg_match_all("/-(.*?)=(\w+|\".*\")/", $cmd, $matches);
echo "<pre>";
print_r($matches);
This gives me this:

Code: Select all

Array
(
    [0] => Array
        (
            [0] => -duration=55
            [1] => -perm=yes
            [2] => -rofl=5
            [3] => -nice="this is a tesT"
        )

    [1] => Array
        (
            [0] => duration
            [1] => perm
            [2] => rofl
            [3] => nice
        )

    [2] => Array
        (
            [0] => 55
            [1] => yes
            [2] => 5
            [3] => "this is a tesT"
        )

)
As you can see everything works except for that it leaves the quotes in tact - I need to get read of the quotes, I could str_replace them off, but that's kind of tacky and thought there might be a more simple way with a regex change that I'm missing.

Any help is appreciated, thanks;
Jeff

Posted: Wed Apr 04, 2007 7:48 am
by feyd

Code: Select all

<?php

$p = '#-([^\s=]+)(?:'.'(?:=|\s+)(?:"(.*?(?<!\\\\)(?:\\\\\\\\)*)"|\'(.*?(?<!\\\\)(?:\\\\\\\\)*)\'|(\w+)))?#';

$cmd = '-duration=55 -perm=yes -rofl=5 -nice="this is\\" a \\\\\\"tesT" JeFFb68CAM test message';

preg_match_all($p, $cmd, $matches);

var_export($matches);

?>
outputs

Code: Select all

array (
  0 =>
  array (
    0 => '-duration=55',
    1 => '-perm=yes',
    2 => '-rofl=5',
    3 => '-nice="this is\\" a \\\\\\"tesT"',
  ),
  1 =>
  array (
    0 => 'duration',
    1 => 'perm',
    2 => 'rofl',
    3 => 'nice',
  ),
  2 =>
  array (
    0 => '',
    1 => '',
    2 => '',
    3 => 'this is\\" a \\\\\\"tesT',
  ),
  3 =>
  array (
    0 => '',
    1 => '',
    2 => '',
    3 => '',
  ),
  4 =>
  array (
    0 => '55',
    1 => 'yes',
    2 => '5',
    3 => '',
  ),
)

Posted: Thu Apr 05, 2007 3:46 am
by stereofrog
Well, to me this looks a bit overcomplicated. ;)

JeFFb68CAM, lets try this

Code: Select all

$re = '/-(\w+)=(?:(\w+)|"(.*?)")/';
This captures the parameter in [2] or [3], leaving the other group empty, so that you can blindly take both

Code: Select all

$param_value = @$mathes[2] . @$matches[3];

Posted: Thu Apr 05, 2007 8:36 am
by feyd
The version I posted allows for toggle level parameters (no specifier) and allows the quoted string to have escapes so it captures the entire string correctly.

Posted: Thu Apr 05, 2007 12:47 pm
by timvw
Most programming languages have a tool with the name getopt(s), which is dedicated to getting arguments.. And i've seen a couple of php implementations too...

Posted: Fri Apr 06, 2007 12:49 am
by JeFFb68CAM
timvw wrote:Most programming languages have a tool with the name getopt(s), which is dedicated to getting arguments.. And i've seen a couple of php implementations too...
Yeah I've seen some of these but they don't support the "quote" feature either, and that's mainly the functionality that I need.

Thanks for the help stereo and frog, I'll give those a try. Feyd unfortunately yours exports results into 3 different arrays which is not exactly what I'm going for, as I want to keep the functionality of this as easy as possible so people can extend on it further.

Posted: Fri Apr 06, 2007 2:29 am
by JeFFb68CAM
I wrote my own function to do what I wanted to do, I'm posting it here for anyone else to use as reference or for critique from you guys.

Code: Select all

$cmd = "-create -topic=\"This is the new topic of the room\" -max_members=10 Support Room";

$params  = array(
    'options' => array(
        'max_members' => 50,
        'topic' => '',
        'create' => false,
    ),
    'room_name' => ''
);

function parseArgs($cmd, &$params)
{
    $cmd = explode(" ", preg_replace('/\s\s+/', ' ', trim($cmd)));
    $keys = array_keys($params);
    for($x=0;$x<count($params);$x++) {
        if($keys[$x] == 'options') {
            $options =& $params['options'];
            while($cmd[0]{0} == '-') {
                $command = array_shift($cmd);
                $option = substr($command, 1);
                if(stristr($option, '=')) {
                    $option = explode('=', $option);
                    $param = $option[0];
                    $value = $option[1];
                    if(!isset($options[$param])) {
                        continue;
                    }
                    if(empty($option[1])) {
                        $options[$param] = false;
                        continue;
                    }
                    if($value{0} == '"') {
                        $value = substr($value, 1);
                        while(substr($cmd[0], -1, 1) != '"') {
                            $value .= " " . array_shift($cmd);
                        }
                        $value .= " " . substr(array_shift($cmd), 0, -1);
                    }
                    $options[$param] = is_numeric($value) ? intval($value) : $value;
                } else {
                    if(!isset($options[$option])) {
                        continue;
                    }
                    $options[$option] = true;
                }
            }
        } else {
            if(isset($cmd[0])) {
                $params[$keys[$x]] = isset($keys[$x+1]) ? array_shift($cmd) : implode(" ", $cmd);
            }
        }
    }
    return true;
}

parseArgs($cmd, $params);

echo "<pre>";
print_r($params);

die();
Which outputs:

Code: Select all

Array
(
    [options] => Array
        (
            [max_members] => 10
            [topic] => This is the new topic of the room
            [create] => 1
        )

    [room_name] => Support Room
)
Basically this does exactly what I wanted and works with any combination or order of the commands.

Thanks for your guys' help anyway,
Jeff

Posted: Fri Apr 06, 2007 8:00 am
by feyd
What you wanted is just not possible for regular expressions to build. You can integrate the arrays after the fact however as the entries in the last three elements are mutually exclusive.

Posted: Fri Apr 06, 2007 8:09 am
by JeFFb68CAM
feyd wrote:What you wanted is just not possible for regular expressions to build. You can integrate the arrays after the fact however as the entries in the last three elements are mutually exclusive.
Yeah I figured that after thinking about how they work and how each ( ) is it's own result. That's why I just settled for a function instead.