Bir dizeyi büyük harflerle bölme


101

Belirli bir karakter kümesinin oluşumundan önce bir dizgiyi bölmenin pitonik yolu nedir ?

Örneğin, 'TheLongAndWindingRoad' herhangi bir büyük harf geçtiğinde (muhtemelen ilki hariç) bölmek ve elde etmek istiyorum ['The', 'Long', 'And', 'Winding', 'Road'].

Düzenleme: Aynı zamanda tek tek oluşumları da bölmelidir, yani 'ABC'elde etmek istiyorum ['A', 'B', 'C'].

Yanıtlar:


143

Ne yazık ki Python'da sıfır genişlikli bir eşleşmeye bölmek mümkün değil . Ancak re.findallbunun yerine şunu kullanabilirsiniz :

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

14
Bunun ilk büyük karakterden önceki tüm karakterleri çıkaracağına dikkat edin. 'theLongAndWindingRoad' ['Long', 'And', 'Winding', 'Road'] ile
sonuçlanacak

17
@MarcSchulder: Bu vakaya ihtiyacınız varsa, sadece '[a-zA-Z][^A-Z]*'normal ifade olarak kullanın .
knub

Upercase olmadan da aynısını yapmak mümkün mü?
Laurent Cesaro

4
Alt deve harflerini bölmek içinprint(re.findall('^[a-z]+|[A-Z][^A-Z]*', 'theLongAndWindingRoad'))
hard_working_ant

35

İşte alternatif bir normal ifade çözümü. Sorun, "bölmeyi yapmadan önce her büyük harften önce nasıl boşluk ekleyebilirim" şeklinde yeniden yorumlanabilir:

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

Bu, diğer çözümlerin çoğunda olmayan tüm boşluk olmayan karakterleri koruma avantajına sahiptir.


\ 1 öncesindeki alanın neden çalıştığını açıklar mısınız? Bölme yöntemi yüzünden mi yoksa normal ifadeyle ilgili bir şey mi?
Lax_Sam

bölünmüş sınırlayıcı varsayılan olarak herhangi bir boşluk dizgesine
atanır

@Lax_Sam Normal ifade ikamesi, herhangi bir büyük harften önce bir boşluk ekler ve ayır () onu alır
vitaly

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

Rexeg'i değiştirmek "It'sATest"için bölmek istiyorsanız["It's", 'A', 'Test']"[A-Z][a-z']*"


+1: İlk olarak ABC'nin çalışmasını sağlamak için. Ayrıca cevabımı şimdi güncelledim.
Mark Byers

>>> re.findall ('[AZ] [az] *', "Ekonominin yaklaşık% 70'i") -----> ['It', 'Economy']
ChristopheD

@Hayalhanemersin OP, alfa olmayan karakterlerin nasıl ele alınacağını söylemiyor.
John La Rooy

1
true, ancak bu geçerli regex yolu da dropsbüyük harfle başlamayan tüm normal (sadece düz alfa) sözcükler. OP'nin niyetinin bu olduğundan şüpheliyim.
ChristopheD

9

@ChristopheD çözümünün bir varyasyonu

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
Güzel bir - bu Latin olmayan karakterlerde de işe yarar. Burada gösterilen normal ifade çözümleri göstermez.
AlexVhr

7

Bir bakış kullanın:

Python 3.7'de bunu yapabilirsiniz:

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

Ve verir:

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

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

veya

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

1
Filtre tamamen gereksizdir ve yakalama grubuyla doğrudan regex ayrılmasına satın alır sana bir şey: [s for s in re.compile(r"([A-Z][^A-Z]*)").split( "TheLongAndWindingRoad") if s]veren['The', 'Long', 'And', 'Winding', 'Road']
SMCI

1
@smci: Bu kullanımı, bir koşuldaki filterliste anlama ile aynıdır. Buna karşı bir şeyin var mı?
Gabe

1
Bunun koşullu bir liste anlama ile değiştirilebileceğini biliyorum, çünkü o kodu henüz gönderdim, sonra sen kopyaladın. İşte liste anlamanın tercih edilebilir olmasının üç nedeni: a) Okunaklı deyim: liste anlamaları daha Pythonic deyimdir ve filter(lambdaconditionfunc, ...)Python 3'te b) 'ye göre soldan sağa daha net okunur , filter()bir yineleyici döndürür. Yani tamamen eşdeğer olmayacaklar. c) Bence filter()de daha yavaş
smci

5

Bence diziyi büyük harfle bitmeyen kelimelere bölmek daha iyi bir cevap olabilir . Bu, dizenin büyük harfle başlamadığı durumu ele alır.

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

misal:

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

4
src = 'TheLongAndWindingRoad'
glue = ' '

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

1
Bunun soruna neden iyi bir çözüm olduğuna bir açıklama ekler misiniz?
Matas Vaitkevicius

Üzgünüm. Son adımı unuttum
user3726655

Bana özlü, pitonik ve açıklayıcı görünüyor.

2

Alternatif çözüm (açık normal ifadelerden hoşlanmıyorsanız):

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

Normal ifade içermeyen bir başkası ve istenirse bitişik büyük harf tutma yeteneği

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

Bu more_itertools.split_beforealetle mümkündür .

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

Ayrıca, tek tek oluşumları da bölmelidir, yani 'ABC'elde etmek istiyorum ['A', 'B', 'C'].

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

more_itertoolstüm orijinal itertools tarifleri için uygulamalar da dahil olmak üzere 60'tan fazla kullanışlı araç içeren ve manuel uygulamalarını ortadan kaldıran bir üçüncü taraf paketidir .


1

Pythonic yolu şöyle olabilir:

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

Re / re2'den kaçınarak Unicode için iyi çalışır.

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

0

Normal ifade veya numaralandırmadan alternatif bir yol:

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

Çok fazla yöntem zincirlemeden veya okunması zor olabilecek uzun bir liste anlayışı kullanmadan daha açık ve daha basit olduğunu düşünüyorum.


0

enumerateVe kullanmanın alternatif bir yoluisupper()

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

Çıktı:

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

0

Yazıyı okuduğumda aklıma gelenleri paylaşmak. Diğer gönderilerden farklı.

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

-1

Verilen içindeki her büyük 'L' harfini boş bir boşluk ve bu "L" harfiyle değiştirin. Bunu liste anlamayı kullanarak yapabiliriz veya bunu yapmak için aşağıdaki gibi bir işlev tanımlayabiliriz.

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

Bir işleve göre gitmeyi seçerseniz, işte nasıl.

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

Verilen örnek durumunda:

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

Ancak çoğu zaman bir cümleyi büyük harflerle böldüğümüzde, genellikle büyük harflerden oluşan sürekli bir akış olan kısaltmaları sürdürmek istediğimiz durumdur. Aşağıdaki kod yardımcı olacaktır.

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

Teşekkürler.


@MarkByers Birisinin cevabımı neden reddettiğini bilmiyorum ama benim için bir bakmanızı çok isterim. Geri bildiriminizi takdir ediyorum.
Samuel Nde
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.