2010-02-17 3 views
54

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

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

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

ответ

78

К сожалению, это невозможно для split on a zero-width match в 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'] 
+5

Опасайтесь, что это приведет к потере любых символов перед первым символом столицы. 'theLongAndWindingRoad' приведет к ['Long', 'And', 'Winding', 'Road'] –

+6

@MarcSchulder: Если вам нужен этот случай, просто используйте '' [a-zA-Z] [^ AZ] * ' 'как регулярное выражение. – knub

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

или

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

Фильтр совершенно не нужен и не покупает ничего по сравнению с прямым разделом регулярных выражений с группой захвата: '[s для s в re.compile (r" ([AZ] [^ AZ] *) "). Split (" TheLongAndWindingRoad ") если s] 'давать' ['', 'Long', 'And', 'Winding', 'Road'] ' – smci

+0

@smci: Это использование' filter' совпадает с пониманием списка с условием. У вас что-нибудь против? – Gabe

+0

Я знаю, что его можно заменить на понимание списка с условием, потому что я просто разместил этот код, а затем вы его скопировали. Вот три причины, список понимание является предпочтительным: а) * Четкий идиома: * списочные являются более Pythonic идиомы и читать более четкую левый направо, чем 'фильтр (lambdaconditionfunc, ...)' б) в Python 3, 'filter()' возвращает итератор. Таким образом, они не будут полностью эквивалентны. c) Я ожидаю, что 'filter()' тоже медленнее – smci

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

+0

+1: для начала работы ABC. Я также обновил свой ответ. –

+0

>>> re.findall ('[A-Z] [a-z] *', «Это около 70% экономики») -----> ['It', 'Economy'] – ChristopheD

+0

@ChristopheD. ОП не говорит, как обрабатывать не-альфа-символы. –

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 
3

Разновидность @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 
+0

Nice one - это работает и с нелатинскими символами. Репрезентативные решения, показанные здесь, не имеют. – AlexVhr

20

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

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

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

2
src = 'TheLongAndWindingRoad' 
glue = ' ' 

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

Не могли бы вы объяснить, почему это является хорошим решением проблемы. –

+0

Прошу прощения. Я забыл последний шаг – user3726655

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

Это возможно с 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 recipes, что исключает их ручной реализации.

Смежные вопросы