文字列を大文字で分割する


94

特定の文字セットが出現する前に文字列を分割するpythonの方法は何ですか?

たとえば'TheLongAndWindingRoad' 、大文字の場合(最初の文字を除く可能性があります)に分割 して、を取得し ['The', 'Long', 'And', 'Winding', 'Road']ます。

編集:それはまた、単一の出現を分割'ABC'する必要があり ['A', 'B', 'C']ます。

回答:


137

残念ながら、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']

13
これにより、最初の大文字の前にある文字が削除されることに注意してください。'theLongAndWindingRoad'は、['Long'、 'And'、 'Winding'、 'Road']になります
Marc Schulder

14
@MarcSchulder:そのケースが必要な場合は'[a-zA-Z][^A-Z]*'、正規表現として使用してください。
knub

大文字なしで同じことをすることは可能ですか?
Laurent Cesaro

2
ラクダの小文字の単語を分割するためprint(re.findall('^[a-z]+|[A-Z][^A-Z]*', 'theLongAndWindingRoad'))
hard_working_ant

32

これが代替の正規表現ソリューションです。この問題は、「分割する前に、各大文字の前にスペースを挿入する方法」と言い換えることができます。

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

これには、空白以外のすべての文字が保持されるという利点がありますが、他のほとんどのソリューションでは保持されません。


\ 1の前のスペースが機能する理由を教えてください。それはsplitメソッドによるものですか、それとも正規表現に関連したものですか?
Lax_Sam 2018

分割区切り文字のデフォルトは任意の空白文字列
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']

"It'sATest"分割し["It's", 'A', 'Test']てrexegを次のように変更したい場合"[A-Z][a-z']*"


+1:まずABCを機能させるため。回答も更新しました。
Mark Byers、2010

>>> re.findall( '[AZ] [az] *'、 "それは経済の約70%" "-----> ['It'、 'Economy']
ChristopheD

@ChristopheD。OPは、非アルファ文字をどのように扱うべきかについては述べていません。
John La Rooy、2010

1
true、ただしこの現在の正規表現の方法dropsは、大文字で始まらないすべての通常の(単なるプレーンアルファの)単語でもあります。それがOPの意図だったとは思えません。
ChristopheD

8

@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
いいですね-これは非ラテン文字でも機能します。ここに示されている正規表現ソリューションはそうではありません。
AlexVhr 2013

7

先読みを使用します。

Python 3.7では、これを行うことができます。

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

そしてそれはもたらす:

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

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

または

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

1
:フィルタは、あなたのキャプチャグループとの直接的な正規表現の分割以上何も全く不要と買いで[s for s in re.compile(r"([A-Z][^A-Z]*)").split( "TheLongAndWindingRoad") if s]与える['The', 'Long', 'And', 'Winding', 'Road']
SMCI

1
@smci:この使用法はfilter、条件付きのリスト内包表記と同じです。それに反対することはありますか?
ゲイブ2013年

1
私はそのコードを投稿したばかりなので、条件付きのリスト内包に置き換えることができることを知っています。):ここでリスト内包が好ましい三つの理由である判読可能イディオム:リストの内包は、よりPython的イディオムであり、明確に左から右へのより読み出すfilter(lambdaconditionfunc, ...)B)はパイソン3には、filter()イテレータを返します。したがって、それらは完全に同等ではありません。c)私filter()も遅いと思います
smci 2013

4
src = 'TheLongAndWindingRoad'
glue = ' '

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

1
これが問題の良い解決策である理由を説明してください。
Matas Vaitkevicius 14

申し訳ありません。私は最後のステップを忘れてしまったんだ
user3726655

私には簡潔で、pythonicで、一目瞭然のようです。

4

より良い答え、文字列を大文字で終わらない単語に分割することかもしれないと思います。これは、文字列が大文字で始まらない場合を処理します。

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

例:

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

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

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

1

これはmore_itertools.split_beforeツールで可能です。

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

また、単一の出現、つまり、'ABC'を取得したいものから分割する必要があり['A', 'B', 'C']ます。

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

more_itertoolsは、元のすべてのitertoolsレシピの実装を含む60以上の便利なツールを含むサードパーティパッケージであり、手動での実装を不要にします


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

enumerateand を使用する別の方法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']

0

投稿を読んで頭に浮かんだことを共有します。他の投稿とは異なります。

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

Pythonicの方法は次のとおりです。

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

Unicodeに適し、re / re2を回避します。

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

-1

指定されたすべての大文字の「L」を、空のスペースとその文字「L」で置き換えます。リスト内包表記を使用してこれを行うか、次のようにそれを行う関数を定義できます。

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

関数で移動する場合は、次のようにします。

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

与えられた例の場合:

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

しかし、ほとんどの場合、文を大文字で分割しているため、通常は大文字の連続したストリームである省略形を維持する必要があります。以下のコードが役立ちます。

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

ありがとう。


@MarkByersなぜ誰かが私の回答に反対票を投じたのかわかりませんが、私があなたにそれを見てほしいと思います。フィードバックをいただければ幸いです。
Samuel Nde
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.