【问题标题】:Capture a Repeating Group in Python using RegEx (see example)使用 RegEx 在 Python 中捕获重复组(参见示例)
【发布时间】:2014-07-02 20:20:54
【问题描述】:

我正在 python 中编写一个正则表达式来捕获 SSI 标记内的内容。

我要解析标签:

<!--#include file="/var/www/localhost/index.html" set="one" -->

分为以下几部分:

  • 标签功能(例如:includeechoset
  • 属性名称,位于= 符号之前
  • 属性值,位于" 之间

问题是我不知道如何获取这些重复组,因为名称/值对可能在标签中出现一次或多次。我在这上面花了几个小时。

这是我当前的正则表达式字符串:

^\<\!\-\-\#([a-z]+?)\s([a-z]*\=\".*\")+? \-\-\>$

它捕获第一组中的include 和第二组中的file="/var/www/localhost/index.html" set="one",但我所追求的是:

group 1: "include"
group 2: "file"
group 3: "/var/www/localhost/index.html"
group 4 (optional): "set"
group 5 (optional): "one"

(continue for every other name="value" pair)


I am using this site to develop my regex

【问题讨论】:

  • 一次捕获所有标签((?:[a-z]=".*")+?) --&gt;$,然后解析它。您的正则表达式也被不必要地转义了!
  • @AdamSmith:这对我不起作用。应用该正则表达式时,我得到了两组:group 0 : e="/tmp/index.html" set="one" --&gt;group 1: e="/tmp/index.html" set="one"
  • 为什么不使用不同的模式呢?它会让事情变得更简单。
  • @PadraicCunningham 我曾想过这样做,但我希望可以不这样做。我没有意识到看起来微不足道的事情需要付出多少努力。
  • 单独的模式非常简单,如果你想从键值对创建一个字典,它会很容易完成。

标签: python regex ssi


【解决方案1】:

抓取所有可以重复的内容,然后单独解析它们。这可能也是命名组的一个很好的用例!

import re

data = """<!--#include file="/var/www/localhost/index.html" set="one" reset="two" -->"""
pat = r'''^<!--#([a-z]+) ([a-z]+)="(.*?)" ((?:[a-z]+?=".+")+?) -->'''

result = re.match(pat, data)
result.groups()
('include', 'file', '/var/www/localhost/index.html', 'set="one" reset="two"')

然后遍历它:

g1, g2, g3, g4 = result.groups()
for keyvalue in g4.split(): # split on whitespace
    key, value = keyvalue.split('=')
    # do something with them

【讨论】:

  • kv = lambda x: x.split('='){key: val for key, val in [kv(x) for x in m.group(4).split()] } 为我提供了字典中所需的一切。谢谢!
  • @NuclearPeon 跳过合并的 lambda!就做dict([x.split("=") for x in m.group(4).split()])
  • 谢谢,我试图这样做,但遇到了一堆错误,所以我辞职了 lambda。这一下子就解决了! 编辑:我应该更清楚
【解决方案2】:

一种方式用new python regex module

#!/usr/bin/python

import regex

s = r'<!--#include file="/var/www/localhost/index.html" set="one" -->'

p = r'''(?x)
    (?>
        \G(?<!^)
      |
        <!-- \# (?<function> [a-z]+ )
    )
    \s+
    (?<key> [a-z]+ ) \s* = \s* " (?<val> [^"]* ) "
'''

matches = regex.finditer(p, s)

for m in matches:
    if m.group("function"):
        print ("function: " + m.group("function"))
    print (" key:   " + m.group("key") + "\n value: " + m.group("val") + "\n")

re 模块的方式:

#!/usr/bin/python

import re

s = r'<!--#include file="/var/www/localhost/index.html" set="one" -->'

p = r'''(?x)
    <!-- \# (?P<function> [a-z]+ )
    \s+
    (?P<params> (?: [a-z]+ \s* = \s* " [^"]* " \s*? )+ )
    -->
'''

matches = re.finditer(p, s)

for m in matches:
    print ("function: " + m.group("function"))
    for param in re.finditer(r'[a-z]+|"([^"]*)"', m.group("params")):
        if param.group(1):
            print (" value: " + param.group(1) + "\n")
        else:
            print (" key:   " + param.group())

【讨论】:

  • +1 用于使用正则表达式模块,尽管您的打印语句需要括号以兼容 python3。
【解决方案3】:

我建议不要使用单个正则表达式来捕获重复组中的每个项目。相反——不幸的是,我不懂 Python,所以我用我理解的语言来回答它,即 Java——我建议首先提取所有属性,然后循环遍历每个项目,如下所示:

   import  java.util.regex.Pattern;
   import  java.util.regex.Matcher;
public class AllAttributesInTagWithRegexLoop  {
   public static final void main(String[] ignored)  {
      String input = "<!--#include file=\"/var/www/localhost/index.html\" set=\"one\" -->";

      Matcher m = Pattern.compile(
         "<!--#(include|echo|set) +(.*)-->").matcher(input);

      m.matches();

      String tagFunc = m.group(1);
      String allAttrs = m.group(2);

      System.out.println("Tag function: " + tagFunc);
      System.out.println("All attributes: " + allAttrs);

      m = Pattern.compile("(\\w+)=\"([^\"]+)\"").matcher(allAttrs);
      while(m.find())  {
         System.out.println("name=\"" + m.group(1) + 
            "\", value=\"" + m.group(2) + "\"");
      }
   }
}

输出:

Tag function: include
All attributes: file="/var/www/localhost/index.html" set="one"
name="file", value="/var/www/localhost/index.html"
name="set", value="one"

这里有一个可能感兴趣的答案:https://stackoverflow.com/a/23062553/2736496


请考虑将Stack Overflow Regular Expressions FAQ 加入书签以供将来参考。

【讨论】:

  • +1 获取关于在 PHP 正则表达式测试器上测试的 Python 正则表达式的 Java 答案。
  • @CasimiretHippolyte:如果您指的是网页 regex101.com,那么它确实可以选择在我选择的 python 中测试正则表达式。
  • @aliteralmind:虽然我不会使用 Java,但我真诚地感谢您为回答所做的努力。我意识到这个问题可能被认为是垃圾邮件,因为有很多问题会提出不同的问题。我一直在阅读有关正则表达式的各种文章,包括 python 正则表达式文档(我已经阅读了不止一次)。很难缠住我的头。谢谢。
  • @AdamSmith 关于 jwz 的俏皮话,只有在一点点知识总是危险的情况下,这才是正确的:“对我们所有人来说,危险的是比我们拥有的更深的艺术装置我们自己。”
  • @NuclearPeon:很高兴为您提供帮助。我只是想表达遍历组的想法,而不是试图在一个大型的大型正则表达式中进行。语言错误,但概念相同。
【解决方案4】:

不幸的是,python 不允许递归正则表达式。
你可以这样做:

import re
string = '''<!--#include file="/var/www/localhost/index.html" set="one" set2="two" -->'''
regexString = '''<!--\#(?P<tag>\w+)\s(?P<name>\w+)="(?P<value>.*?")\s(?P<keyVal>.*)\s-->'''
regex = re.compile(regexString)
match = regex.match(string)
tag = match.group('tag')
name = match.group('name')
value = match.group('value')
keyVal = match.group('keyVal').split()
for item in keyVal:
    key, val in item.split('=')
    # You can now do whatever you want with the key=val pair

【讨论】:

  • ValueError: too many values to unpack (expected 2) 你不能像这样遍历for key, val in item.split。我实际上做了同样的事情。
  • 你说得对,改成只在那儿拆分吧
【解决方案5】:

regex 库允许捕获重复的组(而内置的 re 不允许)。这允许一个简单的解决方案,而不需要外部的 for 循环来解析组。

import regex

string = r'<!--#include file="/var/www/localhost/index.html" set="one" -->'
rgx = regex.compile(
    r'<!--#(?<fun>[a-z]+)(\s+(?<key>[a-z]+)\s*=\s*"(?<val>[^"]*)")+')

match = rgx.match(string)
keys, values = match.captures('key', 'val')
print(match['fun'], *map(' = '.join, zip(keys, values)), sep='\n  ')

给你你所追求的

include
  file = /var/www/localhost/index.html
  set = one

【讨论】:

    猜你喜欢
    • 2014-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-06
    • 2019-10-08
    相关资源
    最近更新 更多