直接通过“re.split”(在 Python 中)将每个相邻的不同数字之间的字符串分开?

Separate a string between each two neighbouring different digits via `re.split` DIRECTLY (in Python)?

提问人:user688486 提问时间:8/15/2023 更新时间:8/21/2023 访问量:97

问:

例如,我想转换为 .当然,这可以在不使用任何正则表达式的情况下在 for 循环中完成,但我想知道这是否可以通过单个正则表达式来完成。目前,我找到了两种方法:"91234 5g5567\t7₇89^"["9","1","2","3","4 5g55","67\t7₇8","9^"]

>>> import re
>>> def way0(char: str):
...     delimiter = ""
...     while True:
...         delimiter += " "
...         if delimiter not in char:
...             substitution = re.compile("([0-9])(?!\\1)([0-9])")
...             replacement = "\\1"+delimiter+"\\2"
...             cin = [char]
...             while True:
...                 cout = []
...                 for term in cin: cout.extend(substitution.sub(replacement,term).split(delimiter))
...                 if cout == cin:
...                     return cin
...                 else:
...                     cin = cout
...
>>> way0("91234 5g5567\t7₇89^")
['9', '1', '2', '3', '4 5g55', '67\t7₇8', '9^']
>>> import functools
>>> way1 = lambda w: ["".join(list(y)) for x, y in itertools.groupby(re.split("(0+|1+|2+|3+|4+|5+|6+|7+|8+|9+)", w), lambda z: z != "") if x]
>>> way1("91234 5g5567\t7₇89^")
['9', '1', '2', '3', '4 5g55', '67\t7₇8', '9^']

然而,两者都不是简洁的(和理想的)。我已经阅读了 的帮助页面;遗憾的是,以下代码未返回所需的输出:way0way1re.split

>>> re.split(r"(\d)(?!\1)(\d)","91234 5g5567\t7₇89^")
['', '9', '1', '', '2', '3', '4 5g5', '5', '6', '7\t7₇', '8', '9', '^']

可以直接解决这个问题(即无需额外转换)吗?(请注意,这里我不关注效率。re.split

之前有一些关于这个主题的问题(例如,两位数不相同的两位数的正则表达式,匹配 2 位数字但不同的数字的正则表达式,以及匹配不相等或反转的数字集的正则表达式),但它们是关于“RegMatch”的。事实上,我的问题是关于“RegSplit”(而不是“RegMatch”或“RegReplace”)。

Python 正则表达式 拆分

评论

0赞 bobble bubble 8/15/2023
两步思路:re.sub(r“(?<=([0-9]))(?=[0-9])(?!\1)“,”-“,s).split(”-“)
1赞 bobble bubble 8/15/2023
如果您只想使用拆分而不使用捕获组,这可能会起作用,但我怀疑它是否更有效:re.split(r“(?=[0-9])(?<=(?!00|11|22|33|44|55|66|77|88|99)[0-9])“, s)
1赞 Nick 8/15/2023
另一种可能性:[m.group(0) for m in re.finditer(r'.*?(?:([0-9])(?=[0-9])(?!\1)|.$)', s)]
0赞 user688486 8/20/2023
@bobblebubble 非常感谢。后一种方式正是我所需要的。(令人惊讶的是,当我用另一种编程语言执行类似物时,“两步思想”反而效率较低。
1赞 Nick 8/20/2023
@user688486,由于负面前瞻所需的捕获组,它使用起来有点困难,尽管如果您在正则表达式中添加第二个捕获组,例如re.findall[t[0] for t in re.findall(r'(.*?(?:([0-9])(?=[0-9])(?!\2)|.$))', s)]

答:

2赞 bobble bubble 8/20/2023 #1

如果您想在不捕获和任何进一步处理的情况下解决此问题,一个想法是仅使用环视,并在方查看不允许两个相同的数字向前看。re.split

(?=[0-9])(?<=(?!00|11|22|33|44|55|66|77|88|99)[0-9])

请参阅 regex101 上的此演示或 tio.run 上的 Python 演示

它的工作方式是显而易见的。环视查找两位数之间的任何位置。在后面的看,如果前面有两个相同的数字,则在后面看前视会阻止匹配(之前)。

我使用了而不是因为不确定 \d 是否与您的 Python 版本中的 unicode 数字匹配[0-9]\d

评论

1赞 user688486 8/20/2023
谢谢。使用 with 也有效,但这实际上只会导致更多的击键。:)\dflags=re.A
1赞 Nick 8/21/2023 #2

您可以使用 re.finditerre.findall 解决这个问题,尽管它有点复杂,因为负面前瞻需要捕获组(因为在其结果中返回捕获组的内容)。findallfindall

s ="91234 5g5567\t7₇89^"

# re.finditer
[m.group(0) for m in re.finditer(r'.*?(?:([0-9])(?=[0-9])(?!\1)|.$)', s)]

# re.findall
[t[0] for t in re.findall(r'(.*?(?:([0-9])(?=[0-9])(?!\2)|.$))', s)]

在这两种情况下,答案都是

['9', '1', '2', '3', '4 5g55', '67\t7₇8', '9^']

tio.run 上的 Python 演示