寻找与我当前函数类似结果的正则表达式模式

5 投票
5 回答
97 浏览
提问于 2025-04-12 13:37

我有一些用帕斯卡命名法(Pascal Case)写的文本,想把它们分成单独的词或标记。比如,"Hello123AIIsCool" 应该变成 ["Hello", "123", "AI", "Is", "Cool"]

一些条件

  • 每个“词”总是以大写字母开头。例如,"Hello"
  • 连续的数字应该保持在一起。例如,"123" 应该变成 ["123"],而不是 ["1", "2", "3"]
  • 连续的大写字母也应该保持在一起,如果最后一个字母是新词的开头,就要分开。比如,"ABCat" 应该变成 ["AB", "Cat"],而不是 ["ABC", "at"]
  • 并不能保证每个条件在字符串中都有匹配的部分。比如,"Hello""HelloAI""HelloAIIsCool""Hello123""123AI""AIIsCool",以及其他我没有提供的组合都是可能的候选者。

我尝试了几种正则表达式的变体。以下两个尝试让我离想要的结果很近,但还不够。

版本 0

import re

def extract_v0(string: str) -> list[str]:
    word_pattern = r"[A-Z][a-z]*"
    num_pattern = r"\d+"
    pattern = f"{word_pattern}|{num_pattern}"
    extracts: list[str] = re.findall(
        pattern=pattern, string=string
    )
    return extracts

string = "Hello123AIIsCool"
extract_v0(string)
['Hello', '123', 'A', 'I', 'Is', 'Cool']

版本 1

import re

def extract_v1(string: str) -> list[str]:
    word_pattern = r"[A-Z][a-z]+"
    num_pattern = r"\d+"
    upper_pattern = r"[A-Z][^a-z]*"
    pattern = f"{word_pattern}|{num_pattern}|{upper_pattern}"
    extracts: list[str] = re.findall(
        pattern=pattern, string=string
    )
    return extracts

string = "Hello123AIIsCool"
extract_v1(string)
['Hello', '123', 'AII', 'Cool']

目前最佳选择

这个方法结合了正则表达式和循环。它能工作,但这是最好的解决方案吗?还是有更高级的正则表达式可以做到这一点?

import re

def extract_v2(string: str) -> list[str]:
    word_pattern = r"[A-Z][a-z]+"
    num_pattern = r"\d+"
    upper_pattern = r"[A-Z][A-Z]*"
    groups = []
    for pattern in [word_pattern, num_pattern, upper_pattern]:
        while string.strip():
            group = re.search(pattern=pattern, string=string)
            if group is not None:
                groups.append(group)
                string = string[:group.start()] + " " + string[group.end():]
            else:
                break
    
    ordered = sorted(groups, key=lambda g: g.start())
    return [grp.group() for grp in ordered]

string = "Hello123AIIsCool"
extract_v2(string)
['Hello', '123', 'AI', 'Is', 'Cool']

5 个回答

2

你可以试试这个正则表达式:

[A-Z](?:[a-z]+|[A-Z]+(?![a-z]))?|\d+

查看这个 测试案例

import re

pattern = r"[A-Z](?:[a-z]+|[A-Z]+(?![a-z]))?|\d+"
text = "Hello123AIIsCoolAndHTML5IsAMarkupLanguage"

print(re.findall(pattern, text))
# ['Hello', '123', 'AI', 'Is', 'Cool', 'And', 'HTML', '5', 'Is', 'A', 'Markup', 'Language']
2

使用 re.subsplit() 方法

import re

def pascal_case_split(identifier):
    return re.sub('([A-Z][a-z]+)', r' \1', re.sub('([A-Z]+)', r' \1', re.sub('([0-9]+)', r' \1', identifier))).split()

a = pascal_case_split("Hello123AIIsCool")
a
['Hello', '123', 'AI', 'Is', 'Cool']

参考链接

3

根据你的版本1:

import re


def extract_v1(string: str) -> list[str]:
    word_pattern = r"[A-Z][a-z]+"
    num_pattern = r"\d+"
    upper_pattern = r"[A-Z]+(?![a-z])"  # Fixed
    pattern = f"{word_pattern}|{num_pattern}|{upper_pattern}"
    extracts: list[str] = re.findall(
        pattern=pattern, string=string
    )
    return extracts


string = "Hello123AIIsCool"
extract_v1(string)

结果:

['Hello', '123', 'AI', 'Is', 'Cool']

这个固定的 upper_pattern 会尽可能多地匹配大写字母,如果遇到小写字母,它会在小写字母前停下来。

撰写回答