Teilen Sie eine Zeichenfolge in Großbuchstaben


94

Was ist die pythonische Methode, um eine Zeichenfolge vor dem Auftreten eines bestimmten Zeichensatzes zu teilen?

Zum Beispiel möchte ich 'TheLongAndWindingRoad' bei jedem Auftreten eines Großbuchstaben (möglicherweise mit Ausnahme des ersten) aufteilen und erhalten ['The', 'Long', 'And', 'Winding', 'Road'].

Bearbeiten: Es sollte auch einzelne Vorkommen aufteilen, dh von denen 'ABC'ich erhalten möchte ['A', 'B', 'C'].

Antworten:


137

Leider ist es in Python nicht möglich, eine Übereinstimmung mit der Breite Null zu teilen . Sie können aber re.findallstattdessen Folgendes verwenden:

>>> 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
Beachten Sie, dass dadurch alle Zeichen vor dem ersten Großbuchstaben gelöscht werden. 'theLongAndWindingRoad' würde zu ['Long', 'And', 'Winding', 'Road'] führen
Marc Schulder

13
@MarcSchulder: Wenn Sie diesen Fall benötigen, verwenden Sie ihn einfach '[a-zA-Z][^A-Z]*'als regulären Ausdruck .
Knub

Ist es möglich, dasselbe ohne Großbuchstaben zu tun?
Laurent Cesaro

2
Um Kleinbuchstaben zu teilenprint(re.findall('^[a-z]+|[A-Z][^A-Z]*', 'theLongAndWindingRoad'))
hard_working_ant

32

Hier ist eine alternative Regex-Lösung. Das Problem kann wie folgt wiederholt werden: "Wie füge ich vor jedem Großbuchstaben ein Leerzeichen ein, bevor ich die Teilung durchführe?"

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

Dies hat den Vorteil, dass alle Nicht-Leerzeichen beibehalten werden, was bei den meisten anderen Lösungen nicht der Fall ist.


Können Sie bitte erklären, warum das Leerzeichen vor \ 1 funktioniert? Liegt es an der Split-Methode oder hat es etwas mit Regex zu tun?
Lax_Sam

Der Split-Begrenzer verwendet standardmäßig eine Leerzeichenfolge
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']

Wenn Sie "It'sATest"teilen möchten , ["It's", 'A', 'Test']um das Rexeg in zu ändern"[A-Z][a-z']*"


+1: Zum ersten Mal ABC zum Laufen bringen. Ich habe jetzt auch meine Antwort aktualisiert.
Mark Byers

>>> re.findall ('[AZ] [az] *', "Es sind ungefähr 70% der Wirtschaft") -----> ['Es', 'Wirtschaft']
ChristopheD

@ChristopheD. Das OP sagt nicht, wie Nicht-Alpha-Zeichen behandelt werden sollen.
John La Rooy

1
stimmt, aber diese aktuelle Regex-Methode auch dropsalle regulären (nur einfachen Alpha) Wörter, die nicht mit einem Großbuchstaben beginnen. Ich bezweifle, dass dies die Absicht des OP war.
ChristopheD

8

Eine Variation der Lösung von @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
Schön - das funktioniert auch mit nicht-lateinischen Zeichen. Die hier gezeigten Regex-Lösungen funktionieren nicht.
AlexVhr

7

Verwenden Sie einen Lookahead:

In Python 3.7 können Sie Folgendes tun:

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

Und es ergibt:

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

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

oder

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

1
Der Filter ist völlig unnötig und kauft Ihnen nichts über eine direkte Regex-Aufteilung mit Erfassungsgruppe: [s for s in re.compile(r"([A-Z][^A-Z]*)").split( "TheLongAndWindingRoad") if s]Geben['The', 'Long', 'And', 'Winding', 'Road']
smci

1
@smci: Diese Verwendung von entspricht filterdem Listenverständnis mit einer Bedingung. Hast du etwas dagegen?
Gabe

1
Ich weiß, dass es durch ein Listenverständnis mit einer Bedingung ersetzt werden kann, da ich diesen Code gerade gepostet habe und Sie ihn dann kopiert haben. Hier sind drei Gründe, warum das Listenverständnis vorzuziehen ist: a) Lesbare Redewendung: Listenverständnis ist eine pythonischere Redewendung und wird von links nach rechts klarer gelesen als filter(lambdaconditionfunc, ...)b) in Python 3 filter()gibt einen Iterator zurück. Sie werden also nicht völlig gleichwertig sein. c) Ich filter()
gehe davon aus,

4
src = 'TheLongAndWindingRoad'
glue = ' '

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

1
Könnten Sie bitte erklären, warum dies eine gute Lösung für das Problem ist?
Matas Vaitkevicius

Es tut mir Leid. Ich habe den letzten Schritt vergessen
user3726655

Scheint mir prägnant, pythonisch und selbsterklärend.

4

Ich denke, dass eine bessere Antwort darin bestehen könnte , die Zeichenfolge in Wörter aufzuteilen, die nicht in einem Großbuchstaben enden. Dies würde den Fall behandeln, in dem die Zeichenfolge nicht mit einem Großbuchstaben beginnt.

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

Beispiel:

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

2

Alternative Lösung (wenn Sie explizite reguläre Ausdrücke nicht mögen):

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

Eine andere ohne Regex und die Fähigkeit, zusammenhängende Großbuchstaben beizubehalten, wenn dies gewünscht wird

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

Dies ist mit dem more_itertools.split_beforeTool möglich.

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

Es sollte auch einzelne Vorkommen aufteilen, dh von denen 'ABC'ich erhalten möchte ['A', 'B', 'C'].

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

more_itertoolsist ein Paket von Drittanbietern mit mehr als 60 nützlichen Tools, einschließlich Implementierungen für alle ursprünglichen itertools-Rezepte , wodurch deren manuelle Implementierung entfällt.


0

Ein alternativer Weg ohne Verwendung von Regex oder Aufzählung:

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

Ich denke, es ist klarer und einfacher, ohne zu viele Methoden zu verketten oder ein langes Listenverständnis zu verwenden, das schwer zu lesen sein kann.


0

Ein alternativer Weg mit enumerateundisupper()

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

Ausgabe:

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

0

Teilen, was mir beim Lesen des Beitrags in den Sinn kam. Anders als andere Beiträge.

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

Der pythonische Weg könnte sein:

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

Funktioniert gut für Unicode und vermeidet re / re2.

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

-1

Ersetzen Sie jeden Großbuchstaben 'L' in der angegebenen durch ein Leerzeichen plus den Buchstaben "L". Wir können dies mithilfe des Listenverständnisses tun oder eine Funktion definieren, um dies wie folgt zu tun.

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

Wenn Sie sich für eine Funktion entscheiden, gehen Sie wie folgt vor.

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

Im Fall des angegebenen Beispiels:

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

Die meiste Zeit, in der wir einen Satz in Großbuchstaben aufteilen, ist es jedoch normalerweise so, dass wir Abkürzungen beibehalten möchten, die normalerweise ein kontinuierlicher Strom von Großbuchstaben sind. Der folgende Code würde helfen.

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

Vielen Dank.


@MarkByers Ich weiß nicht, warum jemand meine Antwort abgelehnt hat, aber ich würde mich freuen, wenn Sie sich das für mich ansehen. Ich würde mich über Ihr Feedback freuen.
Samuel Nde
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.