我有一个字符串,我正试图根据几个regex模式验证它,我希望由于模式匹配在3.10中可用,我可以使用它来代替创建if-else块。
考虑一个字符串'validateString',其可能的值1021102,1.25.32string021。
我尝试的代码如下所示。
match validateString:
case regex1:
print('Matched regex1')
case regex2:
print('Matched regex2')
case regex3:
print('Matched regex3')
对于正则表达式1、2和3,我尝试了字符串正则表达式模式,还重新设置了。编译对象,但它似乎不起作用。
我一直试图在互联网上找到这方面的例子,但似乎找不到任何包含regex模式匹配和新的python模式匹配的例子。
有什么办法能让它工作吗?
谢谢
无法使用正则表达式模式通过结构模式匹配进行匹配(此时)。
发件人:PEP0643:结构模式匹配
PEP 634:结构模式匹配
结构模式匹配以模式的匹配语句和case语句以及相关操作的形式添加。模式包括序列、映射、基本数据类型以及类实例。模式匹配使程序能够从复杂的数据类型中提取信息,对数据结构进行分支,并基于不同形式的数据应用特定的操作。(重点矿山)
这里没有任何提示表明在提供的模式上调用re
模块的匹配/搜索功能旨在用于匹配。
通过阅读实际PEP,您可以了解更多关于结构模式匹配背后的推理:
它们还包括有关如何使用它的大量示例。
这个问题有一个干净的解决方案。只需将正则表达式从不支持它们的case子句中提升到支持任何Python对象的match子句中。
与使用一系列单独的regex测试相比,组合的regex还将为您提供更好的效率。此外,可以在匹配过程中预编译regex以获得最大效率。
这是一个简单标记器的示例:
pattern = re.compile(r'(\d+\.\d+)|(\d+)|(\w+)|(".*)"')
Token = namedtuple('Token', ('kind', 'value', 'position'))
env = {'x': 'hello', 'y': 10}
for s in ['123', '123.45', 'x', 'y', '"goodbye"']:
mo = pattern.fullmatch(s)
match mo.lastindex:
case 1:
tok = Token('NUM', float(s), mo.span())
case 2:
tok = Token('NUM', int(s), mo.span())
case 3:
tok = Token('VAR', env[s], mo.span())
case 4:
tok = Token('TEXT', s[1:-1], mo.span())
case _:
raise ValueError(f'Unknown pattern for {s!r}')
print(tok)
这将输出:
Token(kind='NUM', value=123, position=(0, 3))
Token(kind='NUM', value=123.45, position=(0, 6))
Token(kind='VAR', value='hello', position=(0, 1))
Token(kind='VAR', value=10, position=(0, 1))
Token(kind='TEXT', value='goodbye', position=(0, 9))
通过以详细的格式编写组合正则表达式,可以提高代码的可理解性,并便于添加更多的案例。可以通过命名regex子模式来进一步改进:
pattern = re.compile(r"""(?x)
(?P<float>\d+\.\d+) |
(?P<int>\d+) |
(?P<variable>\w+) |
(?P<string>".*")
""")
可以在这样的匹配/case语句中使用:
for s in ['123', '123.45', 'x', 'y', '"goodbye"']:
mo = pattern.fullmatch(s)
match mo.lastgroup:
case 'float':
tok = Token('NUM', float(s), mo.span())
case 'int':
tok = Token('NUM', int(s), mo.span())
case 'variable':
tok = Token('VAR', env[s], mo.span())
case 'string':
tok = Token('TEXT', s[1:-1], mo.span())
case _:
raise ValueError(f'Unknown pattern for {s!r}')
print(tok)
这是使用if-elif-ered-chain链编写的等效代码:
for s in ['123', '123.45', 'x', 'y', '"goodbye"']:
if (mo := re.fullmatch('\d+\.\d+', s)):
tok = Token('NUM', float(s), mo.span())
elif (mo := re.fullmatch('\d+', s)):
tok = Token('NUM', int(s), mo.span())
elif (mo := re.fullmatch('\w+', s)):
tok = Token('VAR', env[s], mo.span())
elif (mo := re.fullmatch('".*"', s)):
tok = Token('TEXT', s[1:-1], mo.span())
else:
raise ValueError(f'Unknown pattern for {s!r}')
print(tok)
与匹配/大小写相比,if-elif-else链速度较慢,因为它运行多个regex匹配,并且没有预编译。此外,如果没有案例名称,它的可维护性也较差。
因为所有正则表达式都是独立的,所以我们必须使用walrus操作符重复使用赋值表达式来分别捕获所有匹配对象。这与我们只做一个分配的匹配/案例示例相比很尴尬。
正如帕特里克·阿特纳(PatrickArtner)在另一个答案中正确指出的那样,目前还没有官方的方法来做到这一点。希望该特性将在未来的Python版本中引入,并且这个问题可以取消。在此之前:
PEP 634指定结构模式匹配使用==
运算符来评估匹配。我们可以覆盖它。
import re
from dataclasses import dataclass
# noinspection PyPep8Naming
@dataclass
class regex_in:
string: str
def __eq__(self, other: str | re.Pattern):
if isinstance(other, str):
other = re.compile(other)
assert isinstance(other, re.Pattern)
# TODO extend for search and match variants
return other.fullmatch(self.string) is not None
现在你可以做这样的事情:
match regex_in(validated_string):
case r'\d+':
print('Digits')
case r'\s+':
print('Whitespaces')
case _:
print('Something else')
警告#1是您不能将re.compile
'd模式直接传递给案例,因为Python希望基于类进行匹配。您必须先将模式保存在某个地方。
需要注意的是,实际上也不能使用局部变量,因为Python会将其解释为捕获匹配主题的名称。您需要使用虚线名称,例如,将模式放入类或枚举中:
class MyPatterns:
DIGITS = re.compile('\d+')
match regex_in(validated_string):
case MyPatterns.DIGITS:
print('This works, it\'s all digits')
这可以进一步扩展,以提供访问re的简单方法。匹配对象和组。
# noinspection PyPep8Naming
@dataclass
class regex_in:
string: str
match: re.Match = None
def __eq__(self, other: str | re.Pattern):
if isinstance(other, str):
other = re.compile(other)
assert isinstance(other, re.Pattern)
# TODO extend for search and match variants
self.match = other.fullmatch(self.string)
return self.match is not None
def __getitem__(self, group):
return self.match[group]
# Note the `as m` in in the case specification
match regex_in(validated_string):
case r'\d(\d)' as m:
print(f'The second digit is {m[1]}')
print(f'The whole match is {m.match}')
问题内容: 我已经在线阅读了文档和各种教程,但是我对regex在Java中的工作方式仍然感到困惑。我正在尝试做的是创建一个接受字符串类型参数的函数。然后,我想检查传递的字符串是否包含MDCLXVIivxlcdm以外的任何字符。因此,例如,字符串“ XMLVID”应返回false,而“ ABXMLVA”应返回true。 当我通过时,“ XMLIVD”,“ ABXMLVA”和“ XMLABCIX”
我无法运行此代码: 我在Python中找不到匹配关键字。 我在这里找到的:https://www.python.org/dev/peps/pep-0622/#the-match语句 有什么想法吗?
我一直在将if-elif链转换为结构模式匹配,但在反向测试中遇到了困难。 很容易生成与任何支持的模式(文本、类、映射、序列等)匹配的案例。我如何证明一个否定的匹配? 例如,当对象的类型不匹配时,我需要强制它:
字面量 变量 占位符
或者更好的方法是遍历它,为参数的每个索引获取TRUE或FALSE标志 我只知道如何使用matcher.find()进行循环,如果有任何帮助,我将不胜感激