2017-02-15 56 views
1

我有一些像下面这样的代码。它在文件的每一行中搜索一些模式,如果找到,从匹配的模式中提取一些字符串。每条线最多只能匹配一个模式(如果有的话)。文件中有什么只是文本行,有些行有hight = 123,一些介质= 123,以及一些低= 123。python:更好的方法来提取匹配的模式?

with open(file) as r: 
    for line in r: 
     if re.search('high=\d+', line): 
      p = re.search('high=(\d+)', line) 
      high = p.group(1) 
     elif re.search('medium=\d+', line): 
      p = re.search('medium=(\d+)', line) 
      medium = p.group(1) 
     elif re.search('low=\d+', line): 
      p = re.search('low=(\d+)', line) 
      low = p.group(1) 
     ... 

现在我不知道是否我可以没有做的第二搜索中提取匹配的部分,与一些无效码作为一个例子如下所示。搜索到的模式可能彼此完全不同。这里的问题是在re.search()返回true之后是否有方法来提取匹配的部分。

with open(file) as r: 
    for line in r: 
     if re.search('high=(\d+)', line): 
      high = _.group(1) # invalid code, but looking for something like this. 
     elif re.search('medium=(\d+)', line): 
      medium = _.group(1) # invalid code 
     elif re.search('low=(\d+)', line): 
      low = _.group(1)  # invalid code 
     ... 

注意我可以做到以下,但那不是我想要的。

with open(file) as r: 
    for line in r: 
     m = re.search('high=(\d+)', line) 
     if m: 
      high = m.group(1) 
     else: 
      m = re.search('medium=(\d+)', line) 
      if m: 
       medium = m.group(1) 
      else: 
       m = re.search('low=(\d+)', line) 
       if m: 
        low = m.group(1) 
     ... 

我曾试图先编译模式,如下面的,但我得到了错误“NameError:名字‘_’没有定义”。我用python2和python3试了这个。 _.group(1)有一些特殊的表现(在某些情况下它有效)。我可能会在单独的帖子中提出这个问题。

h = re.compile('hight=(\d+)') 
m = re.compile('medium=(\d+)') 
l = re.compile('low=(\d+)') 

with open(file) as r: 
    for line in r: 
     if h.search(line): 
      high = _.group(1) 
     elif m.search(line): 
      medium = _.group(1) 
     elif l.search(line): 
      low = _.group(1) 
     ... 
+0

如果您可以给出一个输入示例以及您期望从中产生的内容,这将有所帮助。 – naktinis

+0

@naktinis谢谢。我添加了一些解释。其实这个问题很简单。在re.search()返回true之后,是否有办法获得匹配模式的部分内容,无论它是什么? Perl可以让你做到这一点(匹配的部分保存在全局变量中)。 – Shiping

+1

Python没有自动分配给全局变量。你可以很容易地编写一个包装函数来实现它,但它并不是内置于语言中的。 –

回答

2

_保持最后执行的语句的结果,当你在一个交互式shell工作。这只是程序环境中的一个普通变量。

因此,如果您不想再次搜索,则必须存储匹配对象,就像您在第3个代码示例中那样。

如果你想避免嵌套IFS,您可以使用继续:

with open(file) as r: 
    for line in r: 
     m = re.search('high=(\d+)', line) 
     if m: 
      high = m.group(1) 
      continue 
     m = re.search('medium=(\d+)', line) 
     if m: 
      medium = m.group(1) 
      continue 
     m = re.search('low=(\d+)', line) 
     if m: 
      low = m.group(1) 
      continue 
     ... 

编辑,回答您的评论:

还有就是做你想做的事,Python中没有一般的方式:

  • 你没有任何结果的自动分配给一个变量,就像在Perl,

  • 你不能写

    如果XXX = YYY:

避免编写=错误,而不是==

虽然,总是有一个办法做到这一点:

import re 


class Matcher: 
    def __init__(self, pattern): 
     self._pattern = pattern 
     self._compiled_pattern = re.compile(pattern) 
     self._match = None 

    def __str__(self): 
     return '<Matcher> %s, matching %s' % (self._pattern, self._match) 

    # match and search apply on the regex, and return the match object 
    def match(self, string): 
     self._match = self._compiled_pattern.match(string) 
     return self._match 

    def search(self, string): 
     self._match = self._compiled_pattern.search(string) 
     return self._match 

    # Other methods apply to the match object 
    def __getattr__(self, attr): 
     return getattr(self._match, attr) 



if __name__ == '__main__': 

    # instead of m = re.compile(...) 
    m = Matcher(r'(high)=(\d+)') 

    lines = ['high=4', 'nothing here'] 


    for line in lines: 
     # you can use search and match just like on the re object 
     if m.search(line): 
      # then you can use all methods of Match objects 
      print(m.groups()) 
      print(m.group(1), m.group(2)) 
      print(m.span()) 

所以,它看起来像你想要的行为!

+0

谢谢。这肯定有效。但我想知道是否有其他选择,如我的第二个代码示例中所示。显然Python缺乏这样的功能。 – Shiping

+0

@Shiping - Python缺乏这个功能,但自己添加它却很简单。这里显示的基于类的实现更正式,有些人会说更正确。但是您可以简单地使用自己的全局变量并编写一个非常简短的函数来执行搜索和更新全局。 –

+0

谢谢。尽管我希望Python具有类似内置的东西,但它以我想要的方式工作。 – Shiping

2

你可以试试这个

import re 
r = re.compile("(high|medium|low)=(\d+)") 
with open(file) as f: 
    for line in f: 
     match = r.search(line) 
     if not match: 
      continue 
     lvl, val = match.groups() 
     if lvl == "high": 
      high = val 
     elif lvl == "medium": 
      medium = val 
     elif lvl == "low": 
      low = val 
+0

谢谢。您发布的内容应该适用于我在代码中展示的案例。但是我要找的是在re.search()返回true之后提取匹配模式的更一般的方式,而不必测试其他任何东西。 – Shiping

+0

@Shiping're.search'返回匹配对象而不是布尔值。即使你用'search'替换'match',上面的代码也可以工作。如果你想测试然后提取,你可以使用'try..except'与上面相同的代码,或者分配're.search'返回的'Match'对象,并检查它是否为'None'。 – adifire

+0

@adifire - 我不认为你的编辑更接近OP所寻找的。其实它可能更糟糕。在主要问题上查看他们对Perl的评论。 –