[英]Using backreferences for regular expressions in Python LXML with XPath
我必须处理具有如下结构的 XML 文档:
<tok xpos="ABCDE">node1</tok> <tok xpos="XYZ">node2</tok>
<tok xpos="ABCDE">node6</tok> <tok xpos="RST">node7</tok>
<tok xpos="ABTSV">node8</tok> <tok xpos="XYU">node9</tok>
<tok xpos="ABTSV">node14</tok> <tok xpos="XBZ">node15</tok>
我需要更改属性“xpos”的部分值,以防以下元素中相同属性的值以特定的字符序列开头。 因此,在此示例中,我需要将任何以“AB”开头的“xpos”属性值的前两个字符替换为一个新值,该值将用“XX”替换“AB”并保留 rest字符串中的字符,以防满足特定条件。 此条件是以下元素中的“xpos”属性的值以字符序列“XY”开头。
因此,经过处理,output 必须是:
<tok xpos="XXCDE">node1</tok> <tok xpos="XYZ">node2</tok>
<tok xpos="ABCDE">node6</tok> <tok xpos="RST">node7</tok>
<tok xpos="XXTSV">node8</tok> <tok xpos="XYU">node9</tok>
<tok xpos="ABTSV">node14</tok> <tok xpos="XBZ">node15</tok>
我试图用下面的代码来做到这一点。 您会注意到,我试图通过对两个子字符串使用括号来使用反向引用,在其中我划分受影响属性的值,然后用 \2 引用第二个捕获组。
source = """
<root>
<tok xpos="ABCDE">node1</tok> <tok xpos="XYZ">node2</tok>
<tok xpos="ABCDE">node6</tok> <tok xpos="RST">node7</tok>
<tok xpos="ABTSV">node8</tok> <tok xpos="XYU">node9</tok>
<tok xpos="ABTSV">node14</tok> <tok xpos="XBZ">node15</tok>
</root>
"""
import lxml.etree
root_element = lxml.etree.XML(source)
for el in root_element.xpath('//tok[starts-with(@xpos, "XY")]/preceding-sibling::tok[1][re:match(@xpos, "^(AB)([A-Z]+)")]',
namespaces={"re": "http://exslt.org/regular-expressions"}):
el.set('xpos', 'XX\2')
这不起作用。 我收到以下错误消息:
ValueError: All strings must be XML compatible: Unicode or ASCII, no NULL bytes or control characters
我该如何实现这个目标? 原则上,根据此https://www.regular-expressions.info/xpath.html XPath 支持反向引用和捕获组。 我只是不知道我应该如何实现它。 我究竟做错了什么?
杰米
注意 python 的lxml
通过 python 的实现支持正则表达式,这与正则表达式的 XPath 风格不同(在Regular-Expressions.info上有描述)。
尽管在某些正则表达式引擎中使用反斜杠作为反向引用,但 python 将首先将字符串中的'\2'
解释为转义序列,特别是对于字符代码 2,即文本开始控制字符。 反斜杠应该被转义( '\\2'
),或者字符串应该是原始的( r'\2'
)。
但是,两者都不会解决最终问题。 首先,不仅反向引用不存储在调用之间,它们也不存储在正则表达式之间; 它们仅在自己的正则表达式中有效。 其次, lxml.etree._Element.set
不支持正则表达式。
在这种情况下,不需要正则表达式; 这些要求很简单,没有它们就可以实现。 您可以使用lxml.etree._Element.get
获取属性值,然后在 Python 中创建新值。
for el in root_element.xpath('//tok[starts-with(@xpos, "XY")]/preceding-sibling::tok[1][starts-with(@xpos, "AB")]'):
el.set('xpos', 'XX' + el.get('xpos')[2:])
我会在 Python 中使用带有 EXSLT 支持的 XSLT 1.0:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:re="http://exslt.org/regular-expressions"
exclude-result-prefixes="re"
version="1.0">
<xsl:param name="pattern">^(AB)([A-Z]+)</xsl:param>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="tok[re:match(@xpos, '^(AB)([A-Z]+)')][following-sibling::tok[1][starts-with(@xpos, 'XY')]]/@xpos">
<xsl:attribute name="{name()}">
<xsl:value-of select="re:replace(., $pattern, '', 'XX$2')"/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.