【问题标题】:Matching key=“value” pattern in PHP string [closed]PHP字符串中的匹配键=“值”模式[关闭]
【发布时间】:2013-04-23 15:24:04
【问题描述】:

如何(最简单的方法)转换像

这样的字符串
oneKey="value 1" key2="value 2" anotherKey="value 3" somekey="value containing spaces"

到一个带有 PHP 的数组(正则表达式与否)?

我想检索这样的值:

$myArray['key']

所以

$myArray['oneKey'] == "value"

所有键都不一样

【问题讨论】:

  • 请出示您尝试过的代码,以便我们提供对您的情况有帮助的具体回复。
  • 我希望你没有试图解析 HTML 属性...
  • 所有键都一样? 'key' 是这里使用的关键字吗?
  • @RaheelHasan 不,键都不同。我已经更新了这个问题。谢谢
  • 这个问题已经在stackoverflow.com/questions/1605606/…得到回答

标签: php regex parsing


【解决方案1】:

匹配带引号的字符串总是很棘手。

假设您的数据从不包含转义的双引号(即作为实际值一部分的双引号,一个简单的

/(?<=^|\s)([^=]+)="([^"]*)"/

...可能会完成这项工作,但不太可能是这种情况。所以我们需要比这更复杂一点,这就是 Friedl 经典的“展开循环”的用武之地:

/(?<=^|\s)([^=\s]+)="((?:[^\\"]|\\.)*)"/

它是如何工作的?好吧,让我们分解一下:

首先,我们从一个lookbehind开始,以验证匹配的开头是字符串的开头还是空格字符:

(?<=^|\s)

接下来我们寻找非空白字符和非等号字符的任意组合(至少其中 1 个)。这是关键,所以我们把它放在一个捕获组中:

([^=\s]+)

接下来我们有一个文字等号和双引号:

="

接下来是“展开循环”。起初这可能有点难以理解,但它可以通过查找任何不是引号字符或转义字符的字符(我选择反斜杠作为转义字符但实际上你可以使用任何字符)来工作, 一个转义字符,后跟任何其他字符。这会重复零次或多次。由于这是值,我们将其包装在一个捕获组中:

((?:[^\\"]|\\.)*)

然后我们简单地用文字双引号结束:

"

把它们放在 PHP 代码中,你会得到这样的结果:

$subject = 'key1="value 1" key2="value 2" key3="value 3" key4="value containing spaces"';

$expr = '/(?<=^|\\s)([^=\\s]+)="((?:[^\\\\"]|\\\\.)*)"/';
preg_match_all($expr, $subject, $matches);

$result = array();
foreach ($matches[1] as $i => $key) {
    $result[$key] = $matches[2][$i];
}

See it working


但这有一个小问题。考虑 what happens 当主题字符串是:

key1="value\" 1"

应该很好很简单,这只是一个转义的引用,对吗?嗯,确实如此,上面的表达式可以轻松处理这种情况。但是看看输出:

Array
(
    [key1] => value\" 1
)

转义字符仍以文字形式存在于结果字符串中。这不是我们想要的。但是上面的表达式只从主题字符串中提取了相关的组件,它根本没有对它们进行插值。为此,我们需要一个单独的过程 - 但现在它只是一个简单的搜索和替换,因为我们已经将字符串分解为我们想要的标记。

所以我们只做这样的事情:

$result = preg_replace_callback('/\\\\./', function($match) {
    switch ($match[0][1]) { // inspect the second character
        // here we can define our special escape sequences, for example:
        case 'r': return "\r";
        case 'n': return "\n";

        // For anything that we don't handle as a special case, we just return
        // the second character in the match, effectively strip the escape
        default: return $match[0][1];
    }
}, $subject);

所以当你把它和上面的代码放在一起时,你会得到一个更像这样的东西:

$subject = 'key1="value \" 1" key2="value \n 2" key3="value 3" key4="value containing spaces"';

$matchExpr = '/(?<=^|\\s)([^=\\s]+)="((?:[^\\\\"]|\\\\.)*)"/';
$replaceExpr = '/\\\\./';

$replaceCallback = function($match) {
    switch ($match[0][1]) {
        case 'r': return "\r";
        case 'n': return "\n";
        default: return $match[0][1];
    }
};

preg_match_all($matchExpr, $subject, $matches);

$result = array();
foreach ($matches[1] as $i => $key) {
    $result[$key] = preg_replace_callback($replaceExpr, $replaceCallback, $matches[2][$i]);
}

See it working

【讨论】:

  • 哇!这就是所谓的优秀答案。非常有用,谢谢!
【解决方案2】:

preg_match_all 试试这个正则表达式:

/(key[0-9]{0,})\="(.+?)"/ims

并且返回的匹配项将具有键和值

【讨论】:

  • key11,key100 怎么样
  • key[0-9]+key\d+ 会更好
  • 真的吗?那么key 没有任何号码呢? (根据他原来的问题)。
  • 无论如何,我总是更喜欢{0,} 而不是* 用于团体(可以说,这就是我滚动的方式:D)
【解决方案3】:
$func = function($string) { $kv = preg_split(/="/, $string); $kv[1] = substr($kv[1], 0, length($kv[1]-1])); $myArray[$kv[0]] = $kv[1]; };
array_map($func, preg_split(/[[:space:]]+/, $string));

【讨论】:

    猜你喜欢
    • 2013-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-13
    • 1970-01-01
    相关资源
    最近更新 更多