python - 如何制作正则表达式 'greedy but optional'

标签 python regex

我正在尝试为表示文件路径的字符串编写解析器,可选地后跟冒号(:)和表示访问标志的字符串(例如r+w)。文件名本身可以包含冒号,例如 foo:bar.txt,因此分隔访问标志的冒号应该是字符串中的最后一个冒号。

这是我迄今为止的实现:

import re

def parse(string):
    SCHEME = r"file://"                             # File prefix
    PATH_PATTERN = r"(?P<path>.+)"                  # One or more of any character
    FLAGS_PATTERN = r"(?P<flags>.+)"        # The letters r, w, a, b, a '+' symbol, or any digit

    # FILE_RESOURCE_PATTERN = SCHEME + PATH_PATTERN + r":" + FLAGS_PATTERN + r"$"               # This makes the first test pass, but the second one fail
    FILE_RESOURCE_PATTERN = SCHEME + PATH_PATTERN + optional(r":" + FLAGS_PATTERN) + r"$"   # This makes the second test pass, but the first one fail

    tokens = re.match(FILE_RESOURCE_PATTERN, string).groupdict()

    return tokens['path'], tokens['flags']

def optional(re):
    '''Encloses the given regular expression in a group which matches 0 or 1 repetitions.'''
    return '({})?'.format(re)

我尝试了以下测试:

import pytest

def test_parse_file_with_colon_in_file_name():
    assert parse("file://foo:bar.txt:r+") == ("foo:bar.txt", "r+")

def test_parse_file_without_acesss_flags():
    assert parse("file://foobar.txt") == ("foobar.txt", None)

if __name__ == "__main__":
    pytest.main([__file__])

问题是,通过使用或不使用可选,我可以使一个或另一个测试通过,但不能同时通过。如果我将 r":"+ FLAGS_PATTERN 设置为可选,则前面的正则表达式将消耗整个字符串。

如何调整 parse 方法以使两个测试都通过?

最佳答案

你应该构建像这样的正则表达式

^file://(?P<path>.+?)(:(?P<flags>[^:]+))?$

请参阅regex demo .

在您的代码中,不需要 ^ anchor ,因为您使用 re.match 将匹配锚定在字符串的开头。 path 组延迟匹配任何 1+ 个字符(因此,可以与第 2 组匹配的所有文本都将出现在第二次捕获中),直到第一次出现 : 后跟 1+ 个除 : 之外的字符(如果存在),然后测试字符串位置的结尾。感谢 $ anchor ,如果第二个可选组不匹配,第一个组将匹配整个字符串。

使用以下修复:

PATH_PATTERN = r"(?P<path>.+?)"                  # One or more of any character
FLAGS_PATTERN = r"(?P<flags>[^:]+)"        # The letters r, w, a, b, a '+' symbol, or any digit

请参阅online Python demo .

关于python - 如何制作正则表达式 'greedy but optional',我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43089832/

相关文章:

python - 当列表超出 Python 范围时返回特定值

python - 如何按列名称对数据框的多个部分进行切片?

Python Pymongo 身份验证失败

java - 删除java中的所有非字母数字字符

javascript - 从html中获取类名的正则表达式

java - 将字符串拆分为矩阵或表格并存储到 ArrayList 中

python - 用IDApython反汇编提取指令

javascript - MAC 地址的正则表达式

regex - 如何在 VIM 中执行存储在文件中的一堆编辑器命令?

python - 如何从 pickle 文件中获取数据到 pandas 数据框中