我希望能够提取查询的标签名称和值。
给定以下查询:
title:(Harry Potter) abc def author:'John' rating:5 jhi cost:"2.20" lmnop qrs
我希望能够提取以下信息:
title => Harry Potter
author => John
rating => 5
cost => 2.20
rest => abc def jhi lmnop qrs
注意标签值可以包含在'..,"……"或(……)。
使用以下命令解决了这个问题:
$query = "..."; // User input
while (preg_match(
'@(?P<key>title|author|rating|cost):(?P<value>[^''"('s]+)@',
$query,
$matches
)) {
echo $matches['key'] . " => " . $matches['value'];
$query = trim(str_replace($matches[0], '', $query));
}
while (preg_match(
'@(?P<key>title|author|rating|cost):[''"(](?P<value>[^''")]+)[''")]@',
$query,
$matches
)) {
echo $matches['key'] . " => " . $matches['value'];
$query = trim(str_replace($matches[0], '', $query));
}
这在很多情况下都是可以的。然而,有相当多的极端情况:
1)例如:
title:(John's) abc
应该转到:
title => John's
rest => abc
而不是
title => (John'
rest => s) abc
2)同时考虑:
title: (foo (: bar)
应该转到:
title => foo (: bar
→:
rest => (foo (bar)
我该怎么做?regex是最好的方法吗?我还能怎么解决这个问题?
UPDATE修复了预期输出中的一个错误
不可能像您那样用一个正则表达式完全解析所有内容,因为您对所有对(键,值)没有相同的规则。例如,标签author中间可以使用右括号,但标签title中间不能使用右括号。单引号可以出现在title中间,但不能出现在author中间,等等。因此,即使您的规则在大多数情况下都有效,您的第二个捕获组也不能正确定义。
改进解决方案的一种方法是为每个标记使用不同的正则表达式。然后你可以这样做:
$str = "title:(foo (: bar) abc def ".
"author:'John' " .
"rating:5 jhi " .
"cost:'"2.20'"" .
"lmnop qrs ";
$regex = array(
"title" => "/(?P<key>title):[[:space:]]*'((?P<value>[^')]*)')/" ,
"author" => "/(?P<key>author):[[:space:]]*'(?P<value>[^']*)'/" ,
"rating" => "/(?P<key>rating):[[:space:]]*(?P<value>['d]+)/" ,
"cost" => "/(?P<key>cost):[[:space:]]*'"(?P<value>['d]+'.['d]{2})'"/"
);
foreach($regex as $k => $r)
{
if(preg_match($r, $str, $matches))
{
echo $matches['key'] . " => " . $matches['value'] . "'n";
}
else
{
echo "Nothing found for " . $k . "'n";
}
}
但是,请注意此解决方案并非万无一失。例如,如果书名包含字符串author: 'JOHN',就会出现问题。
在我看来,避免这种问题的最好方法是为您的输入字符串定义一个语法规则,并拒绝所有不符合您规则的字符串。嗯,我想这也取决于你的要求和你的申请。
编辑
注意标签值可以包含在'..,"……"或(……)。不管用哪个
在这种情况下,你的问题仍然是
['''"'(](?P<value>[^'''"')]+)['''"')]
是不正确的。相反,您希望每对分隔符都匹配。在subpattern中有一个选项
(?|''(?P<value>[^'']+)''|'"(?P<value>[^'"]+)+'"|'((?P<value>[^')]+)'))
如果使用'
作为转义字符,则代码变为
$str = 'title:"foo '" bar" abc def '.
'author:(Joh')n) ' .
'rating:''5''''4'' jhi ' .
'cost:"2.20"' .
'lmnop qrs ';
$regex = "/(?P<key>title|author|rating|cost):[[:space:]]*" .
"(?|" .
"'"(?P<value>(?:(?:'''''")|[^'"])+)'"" . "|" . // matches "..."
"''(?P<value>(?:(?:'''''')|[^''])+)''" . "|" . // matches '...'
"'((?P<value>(?:(?:'''''))|[^')])+)')" . // matches (...)
")/"; // close (?|...
while(preg_match($regex, $str, $matches))
{
echo $matches['key'] . " => " $matches['value'] . "'n";
$str = str_replace($matches[0], '', $str);
}
输出title => foo '" bar
author => Joh')n
rating => 5''4
cost => 2.20