Разделить строку на прописные буквы


каков питонический способ разбить строку до появления заданного набора символов?

например, я хочу разделить 'TheLongAndWindingRoad' при любом появлении прописной буквы (возможно, кроме первой), и получить ['The', 'Long', 'And', 'Winding', 'Road'].

Edit: он также должен разделять отдельные вхождения, т. е. от 'ABC' Я хотел бы получить ['A', 'B', 'C'].

12 61

12 ответов:

к сожалению, это невозможно разделить на матч нулевой ширины в 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']

вот альтернативное решение регулярных выражений. Проблема может быть перефразирована как "как вставить пробел перед каждой прописной буквой, прежде чем делать разделение":

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

это имеет то преимущество, что сохраняются все символы без пробелов, чего нет в большинстве других решений.

>>> 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']*"

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

или

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

вариация на решение @ChristopheD ' s

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

альтернативное решение (если вам не нравятся явные регулярные выражения):

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
src = 'TheLongAndWindingRoad'
glue = ' '

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

другой без регулярных выражений и возможность сохранять непрерывный верхний регистр, если требуется

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']

альтернативный способ без использования регулярного выражения или перечисления:

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(' ')

Я думаю, что это яснее и проще, не связывая слишком много методов или используя длинный список понимания, которые могут быть трудно читать.

альтернативный способ использования 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']

это возможно с more_itertools.split_before.

import more_itertools as mit

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

он также должен разделять отдельные вхождения, т. е. от 'ABC' Я хотел бы получить ['A', 'B', 'C'].

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

more_itertools это сторонний пакет с 60 + полезными инструментами, включая реализации для всех оригинальных модуле itertools рецепты, что исключает их ручную реализацию.

замените каждую прописную букву ' L 'в данном с пустым пространством плюс эту букву "L".

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

в случае данного примера:

print(splitAtUpperCase('TheLongAndWindingRoad')) 
['The', 'Long', 'And', 'Winding', 'Road']

вы также можете использовать for петли с if заявление

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 ' '.join(s.split)
print(splitAtUpperCase(TheLongAndWindingRoad)

>>>> 'The Long And Winding Road'

спасибо.