Quelle est la façon Pythonic de diviser une chaîne avant les occurrences d'un ensemble de caractères donné?
Par exemple, je souhaite diviser 'TheLongAndWindingRoad'
à chaque occurrence d'une lettre majuscule (éventuellement à l'exception de la première), et obtenez ['The', 'Long', 'And', 'Winding', 'Road']
.
Modifier: il doit également diviser les occurrences simples, c'est-à-dire de 'ABC'
J'aimerais obtenir ['A', 'B', 'C']
.
Malheureusement, il n'est pas possible de partager sur une correspondance de largeur nulle en Python. Mais vous pouvez utiliser re.findall
au lieu:
>>> import re
>>> re.findall('[A-Z][^A-Z]*', 'TheLongAndWindingRoad')
['The', 'Long', 'And', 'Winding', 'Road']
>>> re.findall('[A-Z][^A-Z]*', 'ABC')
['A', 'B', 'C']
Voici une solution regex alternative. Le problème peut être reformulé comme "comment insérer un espace avant chaque lettre majuscule, avant de faire le fractionnement":
>>> s = "TheLongAndWindingRoad ABC A123B45"
>>> re.sub( r"([A-Z])", r" \1", s).split()
['The', 'Long', 'And', 'Winding', 'Road', 'A', 'B', 'C', 'A123', 'B45']
Cela a l'avantage de conserver tous les caractères non blancs, ce que la plupart des autres solutions ne font pas.
>>> 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']
Si tu veux "It'sATest"
pour diviser en ["It's", 'A', 'Test']
changez le rexeg en "[A-Z][a-z']*"
Une variation sur la solution de @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
import re
filter(None, re.split("([A-Z][^A-Z]*)", "TheLongAndWindingRoad"))
ou
[s for s in re.split("([A-Z][^A-Z]*)", "TheLongAndWindingRoad") if s]
src = 'TheLongAndWindingRoad'
glue = ' '
result = ''.join(glue + x if x.isupper() else x for x in src).strip(glue).split(glue)
Solution alternative (si vous n'aimez pas les expressions rationnelles explicites):
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
Cela est possible avec more_itertools.split_before
outil.
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']
Il doit également fractionner les occurrences simples, c'est-à-dire de
'ABC'
J'aimerais obtenir['A', 'B', 'C']
.
iterable = "ABC"
[ "".join(i) for i in mit.split_before(iterable, pred=lambda s: s.isupper())]
# ['A', 'B', 'C']
more_itertools
est un paquet tiers avec plus de 60 outils utiles, y compris des implémentations pour tous les originaux recettes itertools , ce qui évite leur implémentation manuelle.
Un autre sans regex et la possibilité de garder des majuscules contiguës si vous le souhaitez
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']
Partager ce qui m'est venu à l'esprit lorsque j'ai lu le post. Différent des autres postes.
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
Utilisez une anticipation:
Dans Python 3.7, vous pouvez le faire:
re.split('(?=[A-Z])', 'theLongAndWindingRoad')
Et cela donne:
['the', 'Long', 'And', 'Winding', 'Road']
Une autre façon d'utiliser enumerate
et isupper()
Code:
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
Sortie:
['The', 'Long', 'And', 'Winding', 'Road']
Une manière alternative sans utiliser regex ou énumérer:
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(' ')
Je pense que c'est plus clair et plus simple sans enchaîner trop de méthodes ou utiliser une longue liste de compréhension qui peut être difficile à lire.