【问题标题】:Parsing PHP strings with quoted values解析带引号的 PHP 字符串
【发布时间】:2012-11-27 01:18:36
【问题描述】:

我想解析如下字符串:

'serviceHits."test_server"."http_test.org" 31987'

到一个数组中:

[0] => serviceHits
[1] => test_server
[2] => http_test.org
[3] => 31987

基本上我想分割成点和空格,将引号内的字符串视为单个值。

这个字符串的格式不固定,这只是一个例子。它可能包含不同数量的元素,在不同的地方带有引号和数字元素。

其他字符串可能如下所示:

test.2 3                       which should parse to [test|2|3]
test."342".cake.2 "cheese"     which should parse to [test|342|cake|2|cheese]
test."red feet".3."green" 4    which should parse to [test|red feet|3|green|4]

有时 oid 字符串可能包含一个引号,如果可能的话应该包含它,但它是解析器中最不重要的部分:

test."a \"b\" c" "cheese face" which should parse to [test|a "b" c|cheese face]

我正在尝试从代理中解析 SNMP OID 字符串,这些字符串是由对 OID 应该是什么样子有不同想法的人编写的,以一种通用的方式。

将 oid 字符串(用点分隔的位)返回值(最后一个值)解析为单独的命名数组会很好。在解析字符串之前简单地分割空格是行不通的,因为 OID 和值都可以包含空格。

谢谢!

【问题讨论】:

  • 你能再举几个你想要解析的字符串的例子吗
  • 还有一些你迄今为止尝试过的例子。这是一个没有明显研究工作的编码请求。
  • 抱歉,我想到目前为止没有人愿意看到我可怕的尝试。我拼凑了一些讨厌的正则表达式,但我没有保留测试文件,因为它们不能正常工作!
  • 感谢您添加更多示例。请在下面查看我提交的内容,因为它通过了您的所有示例并允许转义引号、点和空格。

标签: php regex parsing


【解决方案1】:

我同意这很难找到一个正则表达式来解决这个问题。

这是一个完整的解决方案:

$results = array();
$str = 'serviceHits."test_\"server"."http_test.org" 31987';

// Encode \" to something else temporary
$str_encoded_quotes = strtr($str,array('\\"'=>'####'));

// Split by strings between double-quotes
$str_arr = preg_split('/("[^"]*")/',$str_encoded_quotes,-1,PREG_SPLIT_DELIM_CAPTURE);

foreach ($str_arr as $substr) {

    // If value is a dot or a space, do nothing
    if (!preg_match('/^[\s\.]$/',$substr)) {

        // If value is between double-quotes, it's a string
        // Return as is
        if (preg_match('/^"(.*)"$/',$substr)) {
            $substr = preg_replace('/^"(.*)"$/','\1',$substr); // Remove double-quotes around
            $results[] = strtr($substr,array('####'=>'"'));    // Get escaped double-quotes back inside the string

        // Else, it must be splitted
        } else {
            // Split by dot or space
            $substr_arr = preg_split('/[\.\s]/',$substr,-1,PREG_SPLIT_NO_EMPTY);
            foreach ($substr_arr as $subsubstr)
                $results[] = strtr($subsubstr,array('####'=>'"')); // Get escaped double-quotes back inside string
        }
    }
    // Else, it's an empty substring
}

var_dump($results);

用您所有的新字符串示例进行测试。

第一次尝试(旧)

使用 preg_split :

$str = 'serviceHits."test_server"."http_test.org" 31987';

// -1 : no limit
// PREG_SPLIT_NO_EMPTY : do not return empty results
preg_split('/[\.\s]?"[\.\s]?/',$str,-1,PREG_SPLIT_NO_EMPTY);

【讨论】:

  • 这适用于示例,但不适用于更复杂的字符串。当没有引号(并不总是)时,它似乎没有正确拆分。 'test.45.43.cheese.5 65' 倾斜到 [0] => test.45.43.cheese.5 65
  • 然后添加更多字符串示例:)
【解决方案2】:

最简单的方法可能是用占位符替换字符串中的点和空格,拆分,然后删除占位符。像这样的:

$in = 'serviceHits."test_server"."http_test.org" 31987';

$a = preg_replace_callback('!"([^"]*)"!', 'quote', $in);
$b = preg_split('![. ]!', $a);
foreach ($b as $k => $v) $b[$k] = unquote($v);

print_r($b);


# the functions that do the (un)quoting

function quote($m){
    return str_replace(array('.',' '),
      array('PLACEHOLDER-DOT', 'PLACEHOLDER-SPACE'), $m[1]);
}
function unquote($str){
    return str_replace(array('PLACEHOLDER-DOT', 'PLACEHOLDER-SPACE'),
      array('.',' '), $str);
}

【讨论】:

  • 这似乎适用于我的所有示例,除了引号内的引号,这不是太重要。非常感谢!
【解决方案3】:

这是一个适用于您所有测试样本(加上我自己的一个)的解决方案,并允许您转义引号、点和空格。

由于处理转义码的要求,拆分是不可能的。

虽然可以想象一个正则表达式将整个字符串与 '()' 匹配以标记单独的元素,但我无法使用 preg_matchpreg_match_all 使其工作。

相反,我逐步解析字符串,一次提取一个元素。然后我使用stripslashes 取消转义引号、空格和点。

<?php

$strings = array
(
    'serviceHits."test_server"."http_test.org" 31987',
    'test.2 3',
    'test."342".cake.2 "cheese"',
    'test."red feet".3."green" 4',
    'test."a \\"b\\" c" "cheese face"',
    'test\\.one."test\\"two".test\\ three',
);

foreach ($strings as $string)
{
    print"'{$string}' => " . print_r(parse_oid($string), true) . "\n";
}

/**
 * parse_oid parses and OID and returns an array of the parsed elements.
 * This is an all-or-none function, and will return NULL if it cannot completely
 * parse the string.
 * @param string $string The OID to parse.
 * @return array|NULL A list of OID elements, or null if error parsing.
 */
function parse_oid($string)
{
    $result = array();
    while (true)
    {
        $matches = array();
        $match_count = preg_match('/^(?:((?:[^\\\\\\. "]|(?:\\\\.))+)|(?:"((?:[^\\\\"]|(?:\\\\.))+)"))((?:[\\. ])|$)/', $string, $matches);
        if (null !== $match_count && $match_count > 0)
        {
            // [1] = unquoted, [2] = quoted
            $value = strlen($matches[1]) > 0 ? $matches[1] : $matches[2];

            $result[] = stripslashes($value);

            // Are we expecting any more parts?
            if (strlen($matches[3]) > 0)
            {
                // I do this (vs keeping track of offset) to use ^ in regex
                $string = substr($string, strlen($matches[0]));
            }
            else
            {
                return $result;
            }
        }
        else
        {
            // All or nothing
            return null;
        }
    } // while
}

这会生成以下输出:

'serviceHits."test_server"."http_test.org" 31987' => Array
(
    [0] => serviceHits
    [1] => test_server
    [2] => http_test.org
    [3] => 31987
)

'test.2 3' => Array
(
    [0] => test
    [1] => 2
    [2] => 3
)

'test."342".cake.2 "cheese"' => Array
(
    [0] => test
    [1] => 342
    [2] => cake
    [3] => 2
    [4] => cheese
)

'test."red feet".3."green" 4' => Array
(
    [0] => test
    [1] => red feet
    [2] => 3
    [3] => green
    [4] => 4
)

'test."a \"b\" c" "cheese face"' => Array
(
    [0] => test
    [1] => a "b" c
    [2] => cheese face
)

'test\.one."test\"two".test\ three' => Array
(
    [0] => test.one
    [1] => test"two
    [2] => test three
)

【讨论】:

  • 谢谢!我得看看这个是否比我已经实施的更好。
  • 感谢您查看 - 不要忘记回来并“接受”最符合您原始问题的答案。
  • 我重写了我们的代码以包含您的答案,并且它似乎运行良好。非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多