Python 用于解析版本字符串并查看固定宽度的正则表达式
Python regex for parsing version strings and look behind fixed width
我正在尝试编写一个 Python 库来解析我们的版本格式字符串。 (简化)版本字符串格式如下:
<product>-<x>.<y>.<z>[-alpha|beta|rc[.<n>]][.<extra>]][.centos|redhat|win][.snb|ivb]
这是:
- 产品,即
foo
- 数字版本,即:
0.1.0
- [可选] 预发布信息,即:
beta
、rc.1
、alpha.extrainfo
- [可选]操作系统,即:
centos
- [可选]平台,即:
snb
、ivb
因此以下是有效的版本字符串:
1) foo-1.2.3
2) foo-2.3.4-alpha
3) foo-3.4.5-rc.2
4) foo-4.5.6-rc.2.extra
5) withos-5.6.7.centos
6) osandextra-7.8.9-rc.extra.redhat
7) all-4.4.4-rc.1.extra.centos.ivb
对于所有这些os示例,以下正则表达式工作正常:
^(?P<prod>\w+)-(?P<maj>\d).(?P<min>\d).(?P<bug>\d)(?:-(?P<pre>alpha|beta|rc)(?:\.(?P<pre_n>\d))?(?:\.(?P<pre_x>\w+))?)?(?:\.(?P<os>centos|redhat|win))?(?:\.(?P<plat>snb|ivb))?$
但问题出现在这种类型的版本中(没有 'extra' 预发布信息,但有 os and/or 平台):
8) issue-0.1.0-beta.redhat.snb
使用上述正则表达式,对于字符串 #8,redhat
在预发布的额外信息 pre_x
中被提取,而不是 os
组。
我尝试使用后视来避免在 pre_x
:
中选择 os 或平台字符串
...(?:\.(?P<pre_x>\w+))?(?<!centos|redhat|win|ivb|snb))...
即:
^(?P<prod>\w+)-(?P<maj>\d).(?P<min>\d).(?P<bug>\d)(?:-(?P<pre>alpha|beta|rc)(?:\.(?P<pre_n>\d))?(?:\.(?P<pre_x>\w+))?(?<!centos|redhat|win|ivb|snb))?(?:\.(?P<os>centos|redhat|win))?(?:\.(?P<plat>snb|ivb))?$
如果 Python 的标准模块 re
could accept variable width look behind. I would rather try to stick to the standard module, rather than using regex 这会很好用,因为我的库很可能会分发到大量机器,我想在这些机器上限制依赖性。
我也看过类似的问题:this, this and this 不适用。
关于如何实现这一点有什么想法吗?
我的 regex101 link: https://regex101.com/r/bH0qI7/3
[对于感兴趣的os,这是我实际使用的完整正则表达式:https://regex101.com/r/lX7nI6/2]
您需要使用否定先行断言来使 (?P<pre_x>\w+)
匹配除 centos
或 redhat
之外的任何内容。
^(?P<prod>\w+)-(?P<maj>\d)\.(?P<min>\d)\.(?P<bug>\d)(?:-(?P<pre>alpha|beta|rc)(?:\.(?P<pre_n>\d))?(?:\.(?:(?!centos|redhat)\w)+)?)?(?:\.(?P<os>centos|redhat))?(?:\.(?P<plat>snb|ivb))?$
实际上我会避免使用正则表达式,因为它看起来已经很糟糕了,而且你告诉我们它只是被简化了。手动解析它更具可读性:
def extract(text):
parts = text.split('-')
ret = {}
ret['name'] = parts.pop(0)
ret['version'] = parts.pop(0).split('.')
if len(parts) > 0:
rest_parts = parts.pop(0).split('.')
if rest_parts[-1] in ['snb', 'ivb']:
ret['platform'] = rest_parts.pop(-1)
if rest_parts[-1] in ['redhat', 'centos', 'win']:
ret['os'] = rest_parts.pop(-1)
ret['extra'] = rest_parts
return ret
tests = \
[
'foo-1.2.3',
'foo-2.3.4-alpha',
'foo-3.4.5-rc.2',
'foo-4.5.6-rc.2.extra',
'withos-5.6.7.centos',
'osandextra-7.8.9-rc.extra.redhat',
'all-4.4.4-rc.1.extra.centos.ivb',
'issue-0.1.0-beta.redhat.snb',
]
for test in tests:
print(test, extract(test))
结果:
('foo-1.2.3', {'version': ['1', '2', '3'], 'name': 'foo'})
('foo-2.3.4-alpha', {'version': ['2', '3', '4'], 'name': 'foo', 'extra': ['alpha']})
('foo-3.4.5-rc.2', {'version': ['3', '4', '5'], 'name': 'foo', 'extra': ['rc', '2']})
('foo-4.5.6-rc.2.extra', {'version': ['4', '5', '6'], 'name': 'foo', 'extra': ['rc', '2', 'extra']})
('withos-5.6.7.centos', {'version': ['5', '6', '7', 'centos'], 'name': 'withos'})
('osandextra-7.8.9-rc.extra.redhat', {'version': ['7', '8', '9'], 'os': 'redhat', 'name': 'osandextra', 'extra': ['rc', 'extra']})
('all-4.4.4-rc.1.extra.centos.ivb', {'platform': 'ivb', 'version': ['4', '4', '4'], 'os': 'centos', 'name': 'all', 'extra': ['rc', '1', 'extra']})
('issue-0.1.0-beta.redhat.snb', {'platform': 'snb', 'version': ['0', '1', '0'], 'os': 'redhat', 'name': 'issue', 'extra': ['beta']})
我正在尝试编写一个 Python 库来解析我们的版本格式字符串。 (简化)版本字符串格式如下:
<product>-<x>.<y>.<z>[-alpha|beta|rc[.<n>]][.<extra>]][.centos|redhat|win][.snb|ivb]
这是:
- 产品,即
foo
- 数字版本,即:
0.1.0
- [可选] 预发布信息,即:
beta
、rc.1
、alpha.extrainfo
- [可选]操作系统,即:
centos
- [可选]平台,即:
snb
、ivb
因此以下是有效的版本字符串:
1) foo-1.2.3
2) foo-2.3.4-alpha
3) foo-3.4.5-rc.2
4) foo-4.5.6-rc.2.extra
5) withos-5.6.7.centos
6) osandextra-7.8.9-rc.extra.redhat
7) all-4.4.4-rc.1.extra.centos.ivb
对于所有这些os示例,以下正则表达式工作正常:
^(?P<prod>\w+)-(?P<maj>\d).(?P<min>\d).(?P<bug>\d)(?:-(?P<pre>alpha|beta|rc)(?:\.(?P<pre_n>\d))?(?:\.(?P<pre_x>\w+))?)?(?:\.(?P<os>centos|redhat|win))?(?:\.(?P<plat>snb|ivb))?$
但问题出现在这种类型的版本中(没有 'extra' 预发布信息,但有 os and/or 平台):
8) issue-0.1.0-beta.redhat.snb
使用上述正则表达式,对于字符串 #8,redhat
在预发布的额外信息 pre_x
中被提取,而不是 os
组。
我尝试使用后视来避免在 pre_x
:
...(?:\.(?P<pre_x>\w+))?(?<!centos|redhat|win|ivb|snb))...
即:
^(?P<prod>\w+)-(?P<maj>\d).(?P<min>\d).(?P<bug>\d)(?:-(?P<pre>alpha|beta|rc)(?:\.(?P<pre_n>\d))?(?:\.(?P<pre_x>\w+))?(?<!centos|redhat|win|ivb|snb))?(?:\.(?P<os>centos|redhat|win))?(?:\.(?P<plat>snb|ivb))?$
如果 Python 的标准模块 re
could accept variable width look behind. I would rather try to stick to the standard module, rather than using regex 这会很好用,因为我的库很可能会分发到大量机器,我想在这些机器上限制依赖性。
我也看过类似的问题:this, this and this 不适用。
关于如何实现这一点有什么想法吗?
我的 regex101 link: https://regex101.com/r/bH0qI7/3
[对于感兴趣的os,这是我实际使用的完整正则表达式:https://regex101.com/r/lX7nI6/2]
您需要使用否定先行断言来使 (?P<pre_x>\w+)
匹配除 centos
或 redhat
之外的任何内容。
^(?P<prod>\w+)-(?P<maj>\d)\.(?P<min>\d)\.(?P<bug>\d)(?:-(?P<pre>alpha|beta|rc)(?:\.(?P<pre_n>\d))?(?:\.(?:(?!centos|redhat)\w)+)?)?(?:\.(?P<os>centos|redhat))?(?:\.(?P<plat>snb|ivb))?$
实际上我会避免使用正则表达式,因为它看起来已经很糟糕了,而且你告诉我们它只是被简化了。手动解析它更具可读性:
def extract(text):
parts = text.split('-')
ret = {}
ret['name'] = parts.pop(0)
ret['version'] = parts.pop(0).split('.')
if len(parts) > 0:
rest_parts = parts.pop(0).split('.')
if rest_parts[-1] in ['snb', 'ivb']:
ret['platform'] = rest_parts.pop(-1)
if rest_parts[-1] in ['redhat', 'centos', 'win']:
ret['os'] = rest_parts.pop(-1)
ret['extra'] = rest_parts
return ret
tests = \
[
'foo-1.2.3',
'foo-2.3.4-alpha',
'foo-3.4.5-rc.2',
'foo-4.5.6-rc.2.extra',
'withos-5.6.7.centos',
'osandextra-7.8.9-rc.extra.redhat',
'all-4.4.4-rc.1.extra.centos.ivb',
'issue-0.1.0-beta.redhat.snb',
]
for test in tests:
print(test, extract(test))
结果:
('foo-1.2.3', {'version': ['1', '2', '3'], 'name': 'foo'})
('foo-2.3.4-alpha', {'version': ['2', '3', '4'], 'name': 'foo', 'extra': ['alpha']})
('foo-3.4.5-rc.2', {'version': ['3', '4', '5'], 'name': 'foo', 'extra': ['rc', '2']})
('foo-4.5.6-rc.2.extra', {'version': ['4', '5', '6'], 'name': 'foo', 'extra': ['rc', '2', 'extra']})
('withos-5.6.7.centos', {'version': ['5', '6', '7', 'centos'], 'name': 'withos'})
('osandextra-7.8.9-rc.extra.redhat', {'version': ['7', '8', '9'], 'os': 'redhat', 'name': 'osandextra', 'extra': ['rc', 'extra']})
('all-4.4.4-rc.1.extra.centos.ivb', {'platform': 'ivb', 'version': ['4', '4', '4'], 'os': 'centos', 'name': 'all', 'extra': ['rc', '1', 'extra']})
('issue-0.1.0-beta.redhat.snb', {'platform': 'snb', 'version': ['0', '1', '0'], 'os': 'redhat', 'name': 'issue', 'extra': ['beta']})