0

我正在尝试从看起来类似于的字符串中捕获子字符串

'some string, another string, '

我希望结果匹配组是

('some string', 'another string')

我目前的解决方案

>>> from re import match
>>> match(2 * '(.*?), ', 'some string, another string, ').groups()
('some string', 'another string')

有效,但不切实际——与我在实际项目中所做的相比,我在这里展示的内容在复杂性方面当然大大降低了;我只想使用一种“直接”(非计算)正则表达式模式。不幸的是,到目前为止我的尝试都失败了:

这不匹配(结果为 None),因为 {2} 仅应用于空间,而不是整个字符串:

>>> match('.*?, {2}', 'some string, another string, ')

在重复的字符串周围添加括号会在结果中包含逗号和空格

>>> match('(.*?, ){2}', 'some string, another string, ').groups()
('another string, ',)

添加另一组括号确实可以解决这个问题,但让我感到太多:

>>> match('((.*?), ){2}', 'some string, another string, ').groups()
('another string, ', 'another string')

添加非捕获修饰符可以改善结果,但仍然会丢失第一个字符串

>>> match('(?:(.*?), ){2}', 'some string, another string, ').groups()
('another string',)

我觉得我很接近,但我似乎无法找到正确的方法。

谁能帮我 ?我没有看到任何其他方法?


在前几个响应后更新:

首先非常感谢大家,非常感谢您的帮助!:-)

正如我在原帖中所说,为了描述实际的核心问题,我在我的问题中省略了很多复杂性。对于初学者,在我正在从事的项目中,我正在解析大量不同的基于行的格式的文件(目前每天数万个)(目前为 5 个,很快〜 25 个,可能在数百个之后)。还有 XML、JSON、二进制和其他一些数据文件格式,但让我们保持专注。

为了应对多种文件格式并利用其中许多是基于行的事实,我创建了一个有点通用的 Python 模块,它一个接一个地加载一个文件,对每一行应用一个正则表达式并返回一个大的匹配的数据结构。这个模块是一个原型,出于性能原因,生产版本将需要一个 C++ 版本,它将通过 Boost::Python 连接,并且可能会将正则表达式方言的主题添加到复杂性列表中。

此外,没有 2 次重复,而是在当前 0 和 70(左右)之间变化的数量,逗号并不总是逗号,尽管我最初说过,正则表达式模式的某些部分必须在运行时计算;假设我有理由尝试减少“动态”数量并尽可能多地使用“固定”模式。

所以,一句话:我必须使用正则表达式。


尝试改写:我认为问题的核心归结为:是否存在 Python RegEx 表示法,例如涉及花括号重复并允许我捕获

'some string, another string, '

进入

('some string', 'another string')

?

嗯,这可能把它缩小得太远了——但是,你这样做的任何方式都是错误的:-D


第二次尝试改写:为什么我在结果中看不到第一个字符串('some string')?为什么正则表达式会产生匹配(表明必须有 2 个),但只返回 1 个字符串(第二个)?

即使我使用非数字重复,即使用 + 而不是 {2},问题仍然存在:

>>> match('(?:(.*?), )+', 'some string, another string, ').groups()
('another string',)

此外,它不是返回的第二个字符串,而是最后一个:

>>> match('(?:(.*?), )+', 'some string, another string, third string, ').groups()
('third string',)

再次感谢您的帮助,在试图找出我真正想知道的内容时,同行评审的帮助永远不会停止让我感到惊讶......

4

6 回答 6

5

除非这个问题比你解释的要多得多,否则我看不出使用正则表达式的意义。使用基本的字符串方法容易处理:

[s.strip() for s in mys.split(',') if s.strip()]

或者如果它必须是一个元组:

tuple(s.strip() for s in mys.split(',') if s.strip())

代码也更具可读性。请告诉我这是否不适用。


编辑:好的,这个问题确实比最初看起来的要多。不过,出于历史目的而保留它。(猜我不是“自律”:))

于 2011-03-01T21:38:25.780 回答
4

如上所述,我认为这个正则表达式工作正常:

import re
thepattern = re.compile("(.+?)(?:,|$)") # lazy non-empty match 
thepattern.findall("a, b, asdf, d")     # until comma or end of line
# Result:
Out[19]: ['a', ' b', ' asdf', ' d']

这里的关键是使用findall而不是match. 您的问题的措辞表明您更喜欢match,但它不是这里工作的正确工具 - 它旨在为( )正则表达式中的每个相应组返回一个字符串。由于您的“字符串数”是可变的,因此正确的方法是使用findallsplit

如果这不是您需要的,那么请让问题更具体。

编辑:如果你必须使用元组而不是列表:

tuple(Out[19])
# Result
Out[20]: ('a', ' b', ' asdf', ' d')
于 2011-03-01T22:49:27.800 回答
2
import re

regex = " *((?:[^, ]| +[^, ])+) *, *((?:[^, ]| +[^, ])+) *, *"

print re.match(regex, 'some string, another string, ').groups()
# ('some string', 'another string')
print re.match(regex, ' some string, another string, ').groups()
# ('some string', 'another string')
print re.match(regex, ' some string , another string, ').groups()
# ('some string', 'another string')
于 2011-03-01T21:27:45.873 回答
1

No offense, but you obviously have a lot to learn about regexes, and what you're going to learn, ultimately, is that regexes can't handle this job. I'm sure this particular task is doable with regexes, but then what? You say you have potentially hundreds of different file formats to parse! You even mentioned JSON and XML, which are fundamentally incompatible with regexes.

Do yourself a favor: forget about regexes and learn pyparsing instead. Or skip Python entirely and use a standalone parser generator like ANTLR. In either case, you'll probably find that grammars for most of your file formats have already been written.

于 2011-03-02T01:16:56.743 回答
0

I think the core of the problem boils down to: Is there a Python RegEx notation that e.g. involves curly braces repetitions and allows me to capture 'some string, another string, ' ?

I don't think there is such a notation.

But regexes are not a matter of only NOTATION , that is to say the RE string used to define a regex. It is also a matter of TOOLS, that is to say functions.

Unfortunately, I can't use findall as the string from the initial question is only a part of the problem, the real string is a lot longer, so findall only works if I do multiple regex findalls / matches / searches.

You should give more information without delaying: we could understand more rapidly what are the constraints. Because in my opinion, to answer to your problem as it has been exposed, findall() is indeed OK:

import re

for line in ('string one, string two, ',
             'some string, another string, third string, ',
             # the following two lines are only one string
             'Topaz, Turquoise, Moss Agate, Obsidian, '
             'Tigers-Eye, Tourmaline, Lapis Lazuli, '):

    print re.findall('(.+?), *',line)

Result

['string one', 'string two']
['some string', 'another string', 'third string']
['Topaz', 'Turquoise', 'Moss Agate', 'Obsidian', 'Tigers-Eye', 'Tourmaline', 'Lapis Lazuli']

Now, since you "have omitted a lot of complexity" in your question, findall() could incidentally be unsufficient to hold this complexity. Then finditer() will be used because it allows more flexibility in the selection of groups of a match

import re

for line in ('string one, string two, ',
             'some string, another string, third string, ',
             # the following two lines are only one string
             'Topaz, Turquoise, Moss Agate, Obsidian, '
             'Tigers-Eye, Tourmaline, Lapis Lazuli, '):

    print [ mat.group(1) for mat in re.finditer('(.+?), *',line) ]

gives the same result and can be complexified by writing other expression in place of mat.group(1)

于 2011-03-11T09:56:50.990 回答
-1

为了总结这一点,我似乎已经通过以“动态”方式构建正则表达式模式来使用最佳解决方案:

>>> from re import match
>>> match(2 * '(.*?), ', 'some string, another string, ').groups()
('some string', 'another string')

2 * '(.*?)

就是我所说的动态。替代方法

>>> match('(?:(.*?), ){2}', 'some string, another string, ').groups()
('another string',)

由于以下事实而未能返回所需的结果(正如 Glenn 和 Alan 善意解释的那样)

使用 match,捕获的内容会被捕获组的每次重复覆盖

谢谢大家的帮助!:-)

于 2011-03-10T12:50:15.303 回答