【问题标题】:Python Regex look behindPython Regex 向后看
【发布时间】:2017-06-13 14:44:12
【问题描述】:

我有以下文字:

<clipPath id="p54dfe3d8fa">
   <path d="M 112.176 307.8 
L 112.176 307.8 
L 174.672 270 
L 241.632 171.72 
L 304.128 58.32 
L 380.016 171.72 
L 442.512 217.08 
L 491.616 141.48 
L 491.616 307.8 
z
"/>
  </clipPath>
  <clipPath id="p27c84a8b3c">
   <rect height="302.4" width="446.4" x="72.0" y="43.2"/>
  </clipPath>

我需要把这部分拿出来:

d="M 112.176 307.8 
L 112.176 307.8 
L 174.672 270 
L 241.632 171.72 
L 304.128 58.32 
L 380.016 171.72 
L 442.512 217.08 
L 491.616 141.48 
L 491.616 307.8 
z
"

我需要将此部分替换为其他内容。我能够获取整个 &lt;clipPath ...&gt;&lt;path d="[code i want]"/&gt; 但这对我没有帮助,因为我无法覆盖 &lt;clipPath&gt; 元素中的 id。

请注意,我不想触及其他&lt;clipPath&gt; 元素。我只想更改&lt;clipPath&gt; 元素中的&lt;path&gt; 元素。

我认为答案与选择 clipPath 元素之前的所有内容并在 Path 部分结束有关。任何帮助将不胜感激。

我一直在使用http://pythex.org/ 寻求帮助,并且还看到了奇怪的行为(与多行和空格有关),这些行为与 python 3.x 代码之间的行为不同。

以下是我尝试过的一些事情:

reg = r'(<clipPath.* id=".*".*>)'
reg = re.compile(r'(<clipPath.* id=".*".*>\s*<path.*d="(.*\n)+")')
reg = re.compile(r'((?<!<clipPath).* id=".*".*>\s*<path.*d="(.*\n)+")')

g = reg.search(text)
g

【问题讨论】:

标签: python regex parsing


【解决方案1】:

regex 是 从不解析 xml 的正确方法。

这是一个使用lxml 的简单独立示例:

from lxml import etree

text="""<clipPath id="p54dfe3d8fa">
   <path d="M 112.176 307.8
L 112.176 307.8
L 174.672 270
L 241.632 171.72
L 304.128 58.32
L 380.016 171.72
L 442.512 217.08
L 491.616 141.48
L 491.616 307.8
z
"/>
  </clipPath>
  <clipPath id="p27c84a8b3c">
   <rect height="302.4" width="446.4" x="72.0" y="43.2"/>
  </clipPath>"""

# This creates <metrics>
root = etree.XML("<X>"+text+"</X>")
p = root.find(".//path")
print(p.get("d"))

结果:

M 112.176 307.8 L 112.176 307.8 L 174.672 270 L 241.632 171.72 L 304.128 58.32 L 380.016 171.72 L 442.512 217.08 L 491.616 141.48 L 491.616 307.8 z 
  • 首先,我创建主节点。由于有多个节点,所以我将其包装在任意主节点中
  • 然后我在任何地方寻找“路径”
  • 一旦找到,我就会得到d 属性

现在我正在更改 d 的文本并将其转储:

p.set("d","[new text]")
print(etree.tostring(root))

现在的输出是这样的:

...
<path d="[new text]"/>\n
...

仍然,快速和肮脏,可能对几个 path 节点不健壮,但可以与您提供的 sn-p 一起使用(我不是 xml 专家,只是摸索)

顺便说一句,另一种 hacky/非正则表达式的做法:使用多字符 split:

text.split(' d="')[1].split('"/>')[0]

取d分隔符之后的第二部分,然后是/&gt;分隔符之后的第一部分。保留多行格式。

【讨论】:

  • 很高兴为没有正则表达式的解决方案提供建议。 +1。
  • never say never ;) 使用lxml 或类似的可能更好,但作者也表示他想学习正则表达式
  • @Aaron 我明白,那么 OP 应该练习其他的东西,而不是具有几种可能语法的嵌套语法语言,例如具有逐行数据的文本文件。如果 OP 想用正则表达式解析 C 或 Java,那也是愚蠢的。
  • (另外,由于我对 xml 不了解,而且我可以在几分钟内通过反复试验找到自己的方法,这可以让更多 xml 新手相信它并不难使用,即使我个人讨厌 xml)
  • 根据我的个人经验,我有时会遇到格式不正确的 xml,需要一些 regex lovin'
【解决方案2】:

TL;DR: r'&lt;clipPath.* id="[a-zA-Z0-9]+".*&gt;\s*&lt;path.*d=("(?:.*\n)+?")'

让我们分解一下......

您从:r'(&lt;clipPath.* id=".*".*&gt;\s*&lt;path.*d="(.*\n)+")' 开始,它将您的整个捕获模式包含在一个组中,因此整个元素将被捕获在匹配对象中。让我们去掉那些括号:r'&lt;clipPath.* id=".*".*&gt;\s*&lt;path.*d="(.*\n)+"'

接下来,您似乎经常使用.*,这可能很危险,因为它既盲目又贪婪。对于 clipPath id,如果你知道 id 总是字母数字,更好的解决方案可能是r'&lt;clipPath.* id="[a-zA-Z0-9]+".*&gt;\s*&lt;path.*d="(.*\n)+"'

最后,让我们看看您真正想要捕捉的内容。您的示例显示您想要捕获引号,所以让我们将它们放入我们的捕获组中:...*d=("(.*\n)+")。这给我们留下了一个奇怪的嵌套组情况,所以让我们让内部组不被捕获:...*d=("(?:.*\n)+")

现在我们正在捕捉您想要的东西,但我们仍然有一个问题......如果有多个元素满足这些条件怎么办? +...*d=("(.*\n)+") 中的贪婪匹配将捕获中间的任何一条线。我们可以在这里做的是通过在? 后面跟随...*d=("(?:.*\n)+?") 使+ 不贪婪。

把所有这些东西放在一起:

r'&lt;clipPath.* id="[a-zA-Z0-9]+".*&gt;\s*&lt;path.*d=("(?:.*\n)+?")'

【讨论】:

  • 这几乎直接用正则表达式回答了我的问题,太棒了!我相信 SVG/XML 解析库之一是我最终要使用的,但由于您的努力和解释,这将被标记为正确。谢谢!
【解决方案3】:

一个基于xml 的解决方案,用于编辑路径。

import xml.dom.minidom

# Open XML document using minidom parser
DOMTree = xml.dom.minidom.parseString('<X>' + my_xml + '</X>')
collection = DOMTree.documentElement
for clip_path in collection.getElementsByTagName("clipPath"):
    paths = clip_path.getElementsByTagName('path')
    for path in paths:
        path.setAttribute('d', '[code i want]')

print DOMTree.toxml()

使用的数据:

my_xml = """
    <clipPath id="p54dfe3d8fa">
       <path d="M 112.176 307.8
    L 112.176 307.8
    L 174.672 270
    L 241.632 171.72
    L 304.128 58.32
    L 380.016 171.72
    L 442.512 217.08
    L 491.616 141.48
    L 491.616 307.8
    z
    "/>
      </clipPath>
      <clipPath id="p27c84a8b3c">
       <rect height="302.4" width="446.4" x="72.0" y="43.2"/>
      </clipPath>
"""

【讨论】:

  • 这几乎就是我最终要做的。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-07-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-13
  • 1970-01-01
相关资源
最近更新 更多