我在使用正则表达式时遇到了麻烦。我正在查看一组XML文件,并尝试检测包含换行符的特定节点内的一些文本。
以下是一些示例数据:
<item name='GenMsgText'><text>The signature will be discarded.</text></item>
<item name='GenMsgText'><text>The signature will be discarded.<break/>
Do you want to continue?</text></item>
在该示例中,我想仅捕获第二个节点中的文本。我想出了下面使用第二个正则表达式的解决方案,但我想知道我是否可以只用一个做同样的事情。
if ($content =~m{<item name='GenMsgText'>(<textlist>)?<text>(.*?)</text>}si)
{
$t = $2;
if ($t =~m {\n}i)
{
print G $t."\n\n";
}
}
这是一个不能重复使用的一次性工具,所以我想避免编写任何超过几行的解析代码。此外,上面的代码已经有效了,我问的是个人知识的问题而不是实际使用。
答案 0 :(得分:5)
正则表达式不适合执行此任务,它无法很好地处理嵌套结构。如果你有一个DOM API,这个XPath会找到正确的节点:
如果您正在寻找<break/>
元素,正如您的示例所示:
//item[@name='GenMsgText']/text[break]
对于“真实”换行符,为CR(0xD)或LF(0xA):
//item[@name='GenMsgText']/text[contains(., '
') or contains(., '
')]
答案 1 :(得分:3)
我应该考虑使用一些SAX解析器。正则表达式太脆弱,无法处理xml输入。
答案 2 :(得分:0)
我不确定,但认为这应该有效:
<item name='GenMsgText'>(<textlist>)?<text>(.*\n.*)</text>
答案 3 :(得分:0)
问题是你的模式.*?
可以匹配尖括号和换行符。如果正则表达式开始匹配一个无法匹配的元素,那么就没有什么可以阻止它在下一个元素中继续匹配尝试。如果您知道文本中永远不会有尖括号,您可以将匹配限制为单个元素,如下所示:
<item name='GenMsgText'><text>([^<>\n]*\n[^<>]*)</text></item>
编辑:重要的是要注意Max和Kibbee提供的正则表达式不应用于s模式(/ s,单行,DOTALL ......)。这就是阻止它们超出“item”元素末尾的原因:为了达到下一个元素,它们必须匹配元素之间的行分隔符。
但即使没有/ s修饰符,如果在连续的行上有两个没有内部换行符的元素(即,它们之间只有一个换行符),两个正则表达式都会失败。例如,这两行将匹配为一个:
<item name='GenMsgText'><text>foo</text></item>
<item name='GenMsgText'><text>bar</text></item>
另一方面,如果文本中有两行以上怎么办?其他正则表达式恰好匹配一个换行符,因此它们会失败。在我的正则表达式中,我明确匹配第一个换行符以确保有一个换行符,但如果还有换行符,则它们将与第二个字符类匹配:[^<>]*
这就是为什么我倾向于避免使用.*
或.*?
。
答案 4 :(得分:0)
与Alan提到的相同,您可以使用延迟捕获来仅在匹配结束文本语句之前捕获必要的数据
<item name='GenMsgText'><text>(.*?\n.*?)</text></item>
但同样,正则表达式可能完全是错误的工具,你应该使用正确的XML解析器。