繁体   English   中英

在 Python LXML 和 XPath 中使用正则表达式的反向引用

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM