以大写字母分割字符串


94

在出现给定字符集之前将字符串拆分的pythonic方法是什么?

例如,我想'TheLongAndWindingRoad' 在出现大写字母(可能除了第一个大写字母)时进行拆分 ,并获取 ['The', 'Long', 'And', 'Winding', 'Road']

编辑:它也应该拆分单次出现,即从'ABC'我想获得 ['A', 'B', 'C']

Answers:


137

不幸的是,在Python中无法进行零宽度匹配。但是您可以re.findall改用:

>>> import re
>>> re.findall('[A-Z][^A-Z]*', 'TheLongAndWindingRoad')
['The', 'Long', 'And', 'Winding', 'Road']
>>> re.findall('[A-Z][^A-Z]*', 'ABC')
['A', 'B', 'C']

13
请注意,这会删除第一个大写字母之前的所有字符。'theLongAndWindingRoad'将导致['Long','And','Winding','Road']
Marc Schulder 2016年

14
@MarcSchulder:如果需要这种情况,只需将其'[a-zA-Z][^A-Z]*'用作正则表达式即可。
knub

是否可以在不使用大写字母的情况下执行相同的操作?
Laurent Cesaro '18

2
为了拆分小写的驼峰词print(re.findall('^[a-z]+|[A-Z][^A-Z]*', 'theLongAndWindingRoad'))
hard_working_ant

32

这是替代的正则表达式解决方案。可以将问题重新定义为“在拆分之前,如何在每个大写字母之前插入空格”:

>>> s = "TheLongAndWindingRoad ABC A123B45"
>>> re.sub( r"([A-Z])", r" \1", s).split()
['The', 'Long', 'And', 'Winding', 'Road', 'A', 'B', 'C', 'A123', 'B45']

这样做的优点是保留了所有非空白字符,而大多数其他解决方案则没有。


您能否解释\ 1之前的空格为什么起作用?是因为split方法还是与正则表达式有关?
Lax_Sam '18

拆分定界符默认为任何空格字符串
CIsForCookies

20
>>> import re
>>> re.findall('[A-Z][a-z]*', 'TheLongAndWindingRoad')
['The', 'Long', 'And', 'Winding', 'Road']

>>> re.findall('[A-Z][a-z]*', 'SplitAString')
['Split', 'A', 'String']

>>> re.findall('[A-Z][a-z]*', 'ABC')
['A', 'B', 'C']

如果要"It'sATest"拆分以["It's", 'A', 'Test']将rexeg更改为"[A-Z][a-z']*"


+1:首先让ABC工作。我现在也更新了答案。
Mark Byers 2010年

>>> re.findall('[AZ] [az] *',“大约占经济的70%”)-----> ['It','Economy']
ChristopheD 2010年

@ChristopheD。OP没有说如何处理非字母字符。
John La Rooy 2010年

1
正确,但是这种当前的正则表达式方式也适用于drops所有不以大写字母开头的常规(纯正Alpha)单词。我怀疑那是《任择议定书》的意图。
ChristopheD 2010年

8

@ChristopheD解决方案的一个变体

s = 'TheLongAndWindingRoad'

pos = [i for i,e in enumerate(s+'A') if e.isupper()]
parts = [s[pos[j]:pos[j+1]] for j in xrange(len(pos)-1)]

print parts

2
不错的一种-也适用于非拉丁字符。此处显示的正则表达式解决方案没有。
AlexVhr

7

提前使用:

在Python 3.7中,您可以执行以下操作:

re.split('(?=[A-Z])', 'theLongAndWindingRoad')

它产生:

['the', 'Long', 'And', 'Winding', 'Road']

5
import re
filter(None, re.split("([A-Z][^A-Z]*)", "TheLongAndWindingRoad"))

要么

[s for s in re.split("([A-Z][^A-Z]*)", "TheLongAndWindingRoad") if s]

1
过滤器完全没有必要,并且可以通过捕获组直接进行正则表达式拆分,[s for s in re.compile(r"([A-Z][^A-Z]*)").split( "TheLongAndWindingRoad") if s]['The', 'Long', 'And', 'Winding', 'Road']
而不会给

1
@smci:的用法filter与带条件的列表理解相同。你有反对吗?
加布

1
我知道可以将其替换为带有条件的列表理解,因为我刚刚发布了该代码,然后您将其复制。有以下三个原因使列表理解更为可取:a)清晰易懂的习惯用法:列表理解是一种更Python化的习惯用法,filter(lambdaconditionfunc, ...)在Python 3中比b)从左到右更清晰地读取,filter()返回一个迭代器。因此它们将不会完全等效。c)我预计filter()也会变慢
smci

4
src = 'TheLongAndWindingRoad'
glue = ' '

result = ''.join(glue + x if x.isupper() else x for x in src).strip(glue).split(glue)

1
您能否添加解释说明为什么这是解决问题的好方法。
Matas Vaitkevicius 2014年

对不起。我忘记了最后一步
user3726655

在我看来,简洁明了,精妙易懂。

4

我认为更好的答案可能是将字符串分成不以大写字母结尾的单词。这将处理字符串不以大写字母开头的情况。

 re.findall('.[^A-Z]*', 'aboutTheLongAndWindingRoad')

例:

>>> import re
>>> re.findall('.[^A-Z]*', 'aboutTheLongAndWindingRoadABC')
['about', 'The', 'Long', 'And', 'Winding', 'Road', 'A', 'B', 'C']

2

替代解决方案(如果您不喜欢显式正则表达式):

s = 'TheLongAndWindingRoad'

pos = [i for i,e in enumerate(s) if e.isupper()]

parts = []
for j in xrange(len(pos)):
    try:
        parts.append(s[pos[j]:pos[j+1]])
    except IndexError:
        parts.append(s[pos[j]:])

print parts

1

另一个没有正则表达式,并且如果需要,可以保持连续的大写

def split_on_uppercase(s, keep_contiguous=False):
    """

    Args:
        s (str): string
        keep_contiguous (bool): flag to indicate we want to 
                                keep contiguous uppercase chars together

    Returns:

    """

    string_length = len(s)
    is_lower_around = (lambda: s[i-1].islower() or 
                       string_length > (i + 1) and s[i + 1].islower())

    start = 0
    parts = []
    for i in range(1, string_length):
        if s[i].isupper() and (not keep_contiguous or is_lower_around()):
            parts.append(s[start: i])
            start = i
    parts.append(s[start:])

    return parts

>>> split_on_uppercase('theLongWindingRoad')
['the', 'Long', 'Winding', 'Road']
>>> split_on_uppercase('TheLongWindingRoad')
['The', 'Long', 'Winding', 'Road']
>>> split_on_uppercase('TheLongWINDINGRoadT', True)
['The', 'Long', 'WINDING', 'Road', 'T']
>>> split_on_uppercase('ABC')
['A', 'B', 'C']
>>> split_on_uppercase('ABCD', True)
['ABCD']
>>> split_on_uppercase('')
['']
>>> split_on_uppercase('hello world')
['hello world']

1

使用more_itertools.split_before工具可以做到这一点。

import more_itertools as mit


iterable = "TheLongAndWindingRoad"
[ "".join(i) for i in mit.split_before(iterable, pred=lambda s: s.isupper())]
# ['The', 'Long', 'And', 'Winding', 'Road']

它还应该拆分单个出现的位置,即从'ABC'我想要获得的位置['A', 'B', 'C']

iterable = "ABC"
[ "".join(i) for i in mit.split_before(iterable, pred=lambda s: s.isupper())]
# ['A', 'B', 'C']

more_itertools是带有60多种有用工具的第三方程序包,其中包括所有原始itertools配方的实现,从而避免了它们的手动实现。


0

不使用正则表达式或枚举的另一种方法:

word = 'TheLongAndWindingRoad'
list = [x for x in word]

for char in list:
    if char != list[0] and char.isupper():
        list[list.index(char)] = ' ' + char

fin_list = ''.join(list).split(' ')

我认为这无需链接太多方法或使用难以理解的长列表理解就更清楚,更简单。


0

使用enumerate和的另一种方法isupper()

码:

strs = 'TheLongAndWindingRoad'
ind =0
count =0
new_lst=[]
for index, val in enumerate(strs[1:],1):
    if val.isupper():
        new_lst.append(strs[ind:index])
        ind=index
if ind<len(strs):
    new_lst.append(strs[ind:])
print new_lst

输出:

['The', 'Long', 'And', 'Winding', 'Road']

0

分享我阅读帖子时想到的内容。与其他职位不同。

strs = 'TheLongAndWindingRoad'

# grab index of uppercase letters in strs
start_idx = [i for i,j in enumerate(strs) if j.isupper()]

# create empty list
strs_list = []

# initiate counter
cnt = 1

for pos in start_idx:
    start_pos = pos

    # use counter to grab next positional element and overlook IndexeError
    try:
        end_pos = start_idx[cnt]
    except IndexError:
        continue

    # append to empty list
    strs_list.append(strs[start_pos:end_pos])

    cnt += 1

0

Python方式可能是:

"".join([(" "+i if i.isupper() else i) for i in 'TheLongAndWindingRoad']).strip().split()
['The', 'Long', 'And', 'Winding', 'Road']

适用于Unicode,避免re / re2。

"".join([(" "+i if i.isupper() else i) for i in 'СуперМаркетыПродажаКлиент']).strip().split()
['Супер', 'Маркеты', 'Продажа', 'Клиент']

-1

将给定的每个大写字母“ L”替换为一个空格加上该字母“ L”。我们可以使用列表理解来做到这一点,或者我们可以如下定义一个函数来做到这一点。

s = 'TheLongANDWindingRoad ABC A123B45'
''.join([char if (char.islower() or not char.isalpha()) else ' '+char for char in list(s)]).strip().split()
>>> ['The', 'Long', 'A', 'N', 'D', 'Winding', 'Road', 'A', 'B', 'C', 'A123', 'B45']

如果您选择按功能执行,则方法如下。

def splitAtUpperCase(text):
    result = ""
    for char in text:
        if char.isupper():
            result += " " + char
        else:
            result += char
    return result.split()

在给定示例的情况下:

print(splitAtUpperCase('TheLongAndWindingRoad')) 
>>>['The', 'Long', 'A', 'N', 'D', 'Winding', 'Road']

但是在大多数情况下,我们将句子拆分为大写字母时,通常情况是我们希望保留缩写,这些缩写通常是连续的大写字母流。下面的代码会有所帮助。

def splitAtUpperCase(s):
    for i in range(len(s)-1)[::-1]:
        if s[i].isupper() and s[i+1].islower():
            s = s[:i]+' '+s[i:]
        if s[i].isupper() and s[i-1].islower():
            s = s[:i]+' '+s[i:]
    return s.split()

splitAtUpperCase('TheLongANDWindingRoad')

>>> ['The', 'Long', 'AND', 'Winding', 'Road']

谢谢。


@MarkByers我不知道为什么有人不赞成我的答案,但我希望您能替我看看。非常感谢您的反馈。
塞缪尔·恩德
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.