如何使用正则表达式缩短此表达式

时间:2011-03-18 05:04:57

标签: python regex

我有以下if语句:

if not fileName.startswith(".") and re.search("(.exe|.EXE)$", fileName) is not None and not fileName.endswith("-xyz.exe"):
    pass

基本上,我想检查文件名不是以句点开头,以.exe或.EXE扩展名结尾,但不是以-xyz.exe扩展名结尾。我怎样才能摆脱startwith和endswith,并将这两个检查结合到正则表达式本身。

更新:我问,因为我想了解有关正则表达式的更多信息。根据可读性,我将确定是否值得使其更简洁。

更新2:我遇到了这种情况。我一直在寻找机会了解有关正则表达式的更多信息。这似乎是一个很好的机会,所以我试着亲自动手,直到我遇到困难。请不要给出非正则表达式解决方案或回应Mark Pilgrim关于“现在你有2个问题”的陈述,因为任何人都可以这样做。相反,向我证明现在我有两个问题,就像Mark Pilgrim继续他的课程一样。或者告诉我它很光滑。

6 个答案:

答案 0 :(得分:2)

实际上这个很简单:

if re.search(
    r"""# Always use VERBOSE when composing non-trivial regex!
    ^                 # Anchor to start of string.
    # Apply multiple lookahead assertions from string start:
    (?!\.)            # Assert does NOT begin with dot.
    (?=.*\.exe$)      # Assert DOES end with .EXE
    (?!.*-xyz\.exe$)  # Assert does NOT end with -XYZ.EXE
    .*                # Ok to match the filename (optional).
    """, 
    subject, re.IGNORECASE | re.VERBOSE):
    # Successful match
else:
    # Match attempt failed

编辑:在仔细阅读您的问题之后,您似乎关注的是EXE的情况。在这种情况下,正则表达式也可以轻松处理它:

if re.search(
    r"""# Always use VERBOSE when composing non-trivial regex!
    ^                     # Anchor to start of string.
    # Apply multiple lookahead assertions from string start:
    (?!\.)                # Assert does not begin with dot.
    (?=.*\.(?:exe|EXE)$)  # Assert DOES end with .EXE or .exe
    (?!.*-xyz\.exe$)      # Assert does NOT end with -xyz-exe
    .*                    # Ok to match the filename (optional).
    """, 
    subject, re.VERBOSE):
    # Successful match
else:
    # Match attempt failed

Edit2: John Machin已经指出,使用Python时,当您在寻找只能在目标字符串开头发生的匹配时,请使用^的开头使用re.search方法的字符串断言比使用re.match慢得多(并且被认为是不好的做法)。考虑到这一点,这是一个改进的版本:

if re.match(
    r"""# Always use VERBOSE when composing non-trivial regex!
    # Apply multiple lookahead assertions from string start:
    (?!\.)                # Assert does not begin with dot.
    (?=.*\.(?:exe|EXE)$)  # Assert DOES end with .EXE or .exe
    (?!.*-xyz\.exe$)      # Assert does NOT end with -xyz-exe
    .*                    # Ok to match the filename (optional).
    """, 
    subject, re.VERBOSE):
    # Successful match
else:
    # Match attempt failed

答案 1 :(得分:2)

在开始尝试“缩短”代码之前学习使用基本正则表达式。

这篇文章re.search("(.exe|.EXE)$", fileName)有三个不足之处:

(1)习惯使用原始字符串,即使它没有区别,因为那时你(和你的读者)不需要花时间来确定它是否重要。

(2)未转义.匹配任何字符(换行符除外(默认情况下))。

(3)$在字符串末尾的换行符之前匹配;你应该使用\Z代替。如果不这样做,foo.exe\n(如果您的输入是由未剥离\n的人提供的,则容易错误地获取)将匹配。

您需要的是re.search(r"(\.exe|\.EXE)\Z", fileName)

更新,让所有认为re.search("^blahblah", ...)都是个好主意的人都会受益:

>\python27\python -mtimeit -s"import re;s='x'*100" "re.match(r'foo',s)"
1000000 loops, best of 3: 1.2 usec per loop

>\python27\python -mtimeit -s"import re;s='x'*100" "re.search(r'^foo',s)"
100000 loops, best of 3: 2.91 usec per loop

>\python27\python -mtimeit -s"import re;s='x'*1000" "re.match(r'foo',s)"
1000000 loops, best of 3: 1.2 usec per loop

>\python27\python -mtimeit -s"import re;s='x'*1000" "re.search(r'^foo',s)"
100000 loops, best of 3: 18.5 usec per loop

答案 2 :(得分:1)

我不会使用正则表达式,只需将其包装到多行并使其更智能一点:

if not filename.startswith(".") \
   and filename.lower().endswith(".exe") \
   and not filename.endswith("-xyz.exe"):
    #do stuff

请注意,这与*.eXe*.eXE略有不同,然后扩展的其他混合大小写版本也将被忽略,与原始版本不同。但我敢打赌它不会真的重要,我的测试更好。

编辑:修复了".exe"部分,因为我已经翻了条件,但如果你正在尝试学习正则表达式,这是一个奇怪的人为例子,我认为最好不要尝试和鞋拔正则表达式作为一个问题的解决方案,它不是一个好的解决方案

答案 3 :(得分:1)

你需要使用负面的lookbehind断言:

import re

regex = '[^.].*(?:(?<!-xyz).exe|.EXE)'

vectors = (
  '.123.dat',
  '.123.exe',
  '.123.EXE',
  '123.dat',
  '123.exe',
  '123.EXE',
  '.123-xyz.dat',
  '.123-xyz.exe',
  '.123-xyz.EXE',
  '123-xyz.dat',
  '123-xyz.exe',
  '123-xyz.EXE',
)

for v in vectors:
  print "%s: " % (v,),
  if (bool(re.match(regex, v)) == (not v.startswith(".") and
      re.search("(.exe|.EXE)$", v) is not None and
      not v.endswith("-xyz.exe"))):
    print 'PASS'
  else:
    print 'FAIL'

答案 4 :(得分:1)

import re

pat = re.compile('(?!\.)'
                 '.+'
                 '\.'
                 '(?:(?<!-xyz\.)exe|EXE)'
                 '\Z')

names = ('.123.dat', '.123.exe', '.123.EXE',
         '123.dat', '123.exe', '123.EXE',
         '123-xyz.dat', '123-xyz.exe', '123-xyz.EXE', )

print '\n'.join(v.ljust(18)+str(bool(pat.match(v))) for v in names)

编辑:

你说得对,[^.]优于(?!\.):它更具可读性,逻辑性更强,速度更快,-4%(我测试过)

我还比较了'(?!\.).+?\.(?:EXE|(?<!-xyz)exe)\Z'.+?代替.+

使用此RE,执行时间更长。 额外的时间取决于测试名称中的点数。

在像'78999.abc.juty.123.dat'这样的名字上,它的长度大约增加了15%,在'123.dat'这样的名称上,这是3%更长的时间。 我认为原因是,如果读取的字符是一个点,则每次读取字符后正则表达式电机都会检查。

相反,使用'.+\.'正则表达式电机一直运行到最后,然后返回搜索最后一个点。我认为这是一个正确的解释,因为如果RE '(?!\.).+?\.(?:EXE|(?<!-xyz)exe)\Z''123.teybertyhbeythbeytberyetynetynetnyetnydat'等名称上进行测试,则时间再次更长(+ 30%)

我意识到我的RE与Ignacio的RE非常相似,我想知道为什么我写这个RE,因为它似乎没什么特别的兴趣。事实上,在开始时,我的想法是写'(?!\.).+?(?<=.EXE|(?<!-xyz).exe)\Z',然后我写了另一个字符串。顺便说一句,通过这个被放弃的RE,短名称的执行时间延长了25%,而长名称的执行时间延长了74%。

最后,当我测试执行时间时, Ignacio的解决方案在短名称('123.dat')上延长25%,在长名称上延长47%('78999.abc.juty.123.dat' )

最佳正则表达式

pat = re.compile('[^.]'
                 '.+'
                 '\.'
                 '(?:(?<!-xyz\.)exe|EXE)'
                 '\Z') 

我允许'.+',而不是'.*'替换,因为如果我们希望名称以'.exe''.EXE'结尾,则名称中必须至少包含4个字符

答案 5 :(得分:0)

这将涉及访问每个不以'。'开头的文件。或以'-xyz.exe'结尾。正则表达式模块无法解析其命名空间之外的内容。我不认为这是可能的,但您是否尝试过检查模块的文档?