Jaki jest pythonowy sposób dzielenia łańcucha przed wystąpieniami danego zestawu znaków?
Na przykład chcę podzielić
'TheLongAndWindingRoad'
w każdym przypadku wystąpienia dużej litery (prawdopodobnie z wyjątkiem pierwszej) i uzyskać
['The', 'Long', 'And', 'Winding', 'Road']
.
Edycja: Powinien również podzielić pojedyncze wystąpienia, tj. 'ABC'
Z chciałbym uzyskać
['A', 'B', 'C']
.
'[a-zA-Z][^A-Z]*'
jako wyrażenia regularnego.print(re.findall('^[a-z]+|[A-Z][^A-Z]*', 'theLongAndWindingRoad'))
Oto alternatywne rozwiązanie regex. Problem można powtórzyć w postaci „jak wstawić spację przed każdą wielką literą przed wykonaniem podziału”:
>>> s = "TheLongAndWindingRoad ABC A123B45" >>> re.sub( r"([A-Z])", r" \1", s).split() ['The', 'Long', 'And', 'Winding', 'Road', 'A', 'B', 'C', 'A123', 'B45']
Ma to tę zaletę, że zachowuje wszystkie znaki inne niż białe znaki, czego nie ma większość innych rozwiązań.
źródło
>>> 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']
Jeśli chcesz
"It'sATest"
podzielić, aby["It's", 'A', 'Test']
zmienić rexeg na"[A-Z][a-z']*"
źródło
drops
regularne oznacza również wszystkie zwykłe (tylko zwykłe alfa) słowa, które nie rozpoczynają się od dużej litery. Wątpię, żeby taki był zamiar PO.Odmiana rozwiązania @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
źródło
Użyj lookahead:
W Pythonie 3.7 możesz to zrobić:
re.split('(?=[A-Z])', 'theLongAndWindingRoad')
I daje:
['the', 'Long', 'And', 'Winding', 'Road']
źródło
import re filter(None, re.split("([A-Z][^A-Z]*)", "TheLongAndWindingRoad"))
lub
[s for s in re.split("([A-Z][^A-Z]*)", "TheLongAndWindingRoad") if s]
źródło
[s for s in re.compile(r"([A-Z][^A-Z]*)").split( "TheLongAndWindingRoad") if s]
['The', 'Long', 'And', 'Winding', 'Road']
filter
jest takie samo jak rozumienie listy z warunkiem. Czy masz coś przeciwko temu?filter(lambdaconditionfunc, ...)
b) w Pythonie 3filter()
zwraca iterator. Więc nie będą one całkowicie równoważne. c) Spodziewam się, żefilter()
też wolniejMyślę, że lepszą odpowiedzią może być podzielenie łańcucha na słowa, które nie kończą się wielką literą. Byłoby to obsługiwane w przypadku, gdy ciąg nie zaczyna się od dużej litery.
re.findall('.[^A-Z]*', 'aboutTheLongAndWindingRoad')
przykład:
>>> import re >>> re.findall('.[^A-Z]*', 'aboutTheLongAndWindingRoadABC') ['about', 'The', 'Long', 'And', 'Winding', 'Road', 'A', 'B', 'C']
źródło
src = 'TheLongAndWindingRoad' glue = ' ' result = ''.join(glue + x if x.isupper() else x for x in src).strip(glue).split(glue)
źródło
Alternatywne rozwiązanie (jeśli nie lubisz jawnych wyrażeń regularnych):
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
źródło
Kolejny bez wyrażenia regularnego i możliwość zachowania ciągłych wielkich liter, jeśli jest taka potrzeba
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']
źródło
Jest to możliwe dzięki
more_itertools.split_before
narzędziu.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']
iterable = "ABC" [ "".join(i) for i in mit.split_before(iterable, pred=lambda s: s.isupper())] # ['A', 'B', 'C']
more_itertools
to pakiet innej firmy z ponad 60 użytecznymi narzędziami, w tym implementacjami dla wszystkich oryginalnych receptur itertools , co zapobiega ich ręcznej implementacji.źródło
Pythonic może wyglądać następująco:
"".join([(" "+i if i.isupper() else i) for i in 'TheLongAndWindingRoad']).strip().split() ['The', 'Long', 'And', 'Winding', 'Road']
Działa dobrze dla Unicode, unikając ponownego / re2.
"".join([(" "+i if i.isupper() else i) for i in 'СуперМаркетыПродажаКлиент']).strip().split() ['Супер', 'Маркеты', 'Продажа', 'Клиент']
źródło
Alternatywny sposób bez użycia wyrażenia regularnego lub wyliczenia:
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(' ')
Myślę, że jest to jaśniejsze i prostsze bez łączenia w łańcuch zbyt wielu metod lub używania długiej listy, która może być trudna do odczytania.
źródło
Alternatywny sposób używania
enumerate
iisupper()
Kod:
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
Wynik:
['The', 'Long', 'And', 'Winding', 'Road']
źródło
Dzielenie się tym, co przyszło mi do głowy, kiedy czytałem post. Różni się od innych postów.
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
źródło
Zastąp każdą wielką literę „L” w podanym pustą spacją plus tę literę „L”. Możemy to zrobić za pomocą funkcji list złożonych lub możemy zdefiniować funkcję wykonującą to w następujący sposób.
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']
Jeśli zdecydujesz się skorzystać z funkcji, oto jak to zrobić.
def splitAtUpperCase(text): result = "" for char in text: if char.isupper(): result += " " + char else: result += char return result.split()
W przypadku podanego przykładu:
print(splitAtUpperCase('TheLongAndWindingRoad')) >>>['The', 'Long', 'A', 'N', 'D', 'Winding', 'Road']
Ale w większości przypadków, gdy dzielimy zdanie na duże litery, zwykle jest tak, że chcemy zachować skróty, które zazwyczaj są ciągłym ciągiem wielkich liter. Poniższy kod mógłby pomóc.
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']
Dzięki.
źródło