我感到羞愧,但我仍然不清楚一些正面的方面。
我需要解析包含许多@"I'm a string"
格式的字符串文字的文本文件。
我编写了简单的模式/@"([^"]*)"/si
。它工作正常,preg_match_all返回一个集合。但是,如果字符串文字包含@"I'm plain string. I'm \"qouted\" string "
之类的转义引号,它显然无法正常工作。感谢任何线索。
答案 0 :(得分:2)
这是Freidl经典“展开循环”的一个用例:( EDIT 固定分组用于捕获)
/"((?:[^"\\]|\\.)*)"/
这将匹配引用的字符串,并将反斜杠转义的引号考虑在内。
用于匹配字段(包括@
)的完整正则表达式将是:
/@"((?:[^"\\]|\\.)*)"/
但要小心!我经常看到人们抱怨这种模式在PHP中不起作用,这是因为在字符串中使用反斜杠会略微融化思维。
上述模式中的反斜杠表示需要传递到PCRE的文字反斜杠。这意味着在PHP字符串中使用它们时需要进行双重转义:
$expr = '/@"((?:[^"\\\\]|\\\\.)*)"/';
preg_match_all($expr, $subject, $matches);
print_r($matches[1]); // this will show the content of all the matched fields
它是如何运作的?
......我听到你问。那么,让我们看看我是否能够以一种真正有意义的方式解释这一点。让我们启用x
模式,这样我们就可以将它分开:
/
@ # literal @
" # literal "
( # start capture group, we want everything between the quotes
(?: # start non-capturing group (a group we can safely repeat)
[^"\\] # match any character that's not a " or a \
| # ...or...
\\. # a literal \ followed by any character
)* # close non-capturing group and allow zero or more occurrences
) # close the capture group
" # literal "
/x
这些非常重要的一点是:
[^"\\]|\\.
- 意味着每个反斜杠都是“平衡的” - 每个反斜杠都必须转义一个字符,并且不会多次考虑任何字符。*
重复组中包含上述内容意味着上述模式可以无限次发生,并且允许空字符串(如果您不想允许空字符串,请更改{{ 1}}到*
)。这是“展开循环”的“循环”部分。但是输出字符串仍然包含逃避引号的反斜杠?
确实如此,这只是一个匹配程序,它不会修改匹配。但由于结果是字符串的内容,因此简单的+
将是安全的并产生正确的结果。
然而,在做这种事情时,我经常发现我也想处理其他转义序列 - 在这种情况下,我通常会对结果执行类似的操作:
str_replace('\\"', '"', $result)
这给出了与PHP中双引号字符串类似的行为,其中 preg_replace_callback('/\\./', function($match) {
switch ($match[0][1]) { // inspect the escaped character
case 'r':
return "\r";
case 'n':
return "\n";
case 't':
return "\t";
case '\\':
return '\\';
case '"':
return '"';
default: // if it's not a valid escape sequence, treat the \ as literal
return $match[0];
}
}, $result);
替换为制表符,\t
替换为换行符,依此类推。
如果我还想允许单引号字符串怎么办?
很长一段时间以来,这一直困扰着我。我总是有一种傻笑的感觉,这可以通过反向引用更有效地处理,但是许多尝试都未能产生任何可行的结果。
我这样做:
\n
正如您所看到的,这基本上只是两次使用基本相同的模式,具有OR关系。这也使PHP端的字符串提取变得非常复杂:
/(?:"((?:[^"\\]|\\.)*)")|(?:'((?:[^'\\]|\\.)*)')/
答案 1 :(得分:0)
你需要使用负面的lookbehind - 匹配所有内容,直到找到一个前面没有反斜杠的引号。这是在java:
public static void main(String[] args) {
final String[] strings = new String[]{"@\"I'm a string\"", "@\"I'm plain string. I'm \\\"qouted\\\" \""};
final Pattern p = Pattern.compile("@\"(.*)\"(?<!\\\\)");
System.out.println(p.pattern());
for (final String string : strings) {
final Matcher matcher = p.matcher(string);
while (matcher.find()) {
System.out.println(matcher.group(1));
}
}
}
输出:
I'm a string
I'm plain string. I'm \"qouted\"
模式(没有所有Java转义)是:@"(.*)"(?<!\\)