अपरकेस अक्षरों में एक स्ट्रिंग विभाजित करें


94

वर्णों के दिए गए सेट की घटनाओं से पहले स्ट्रिंग को विभाजित करने का पायथोनिक तरीका क्या है ?

उदाहरण के लिए, मैं 'TheLongAndWindingRoad' एक अपरकेस अक्षर की किसी भी घटना को विभाजित करना चाहता हूं (संभवतः पहले को छोड़कर), और प्राप्त करता हूं ['The', 'Long', 'And', 'Winding', 'Road']

संपादित करें: इसे एकल घटनाओं को भी विभाजित करना चाहिए, अर्थात जहां से 'ABC'मैं प्राप्त करना चाहूंगा ['A', 'B', 'C']

जवाबों:


137

दुर्भाग्य से पायथन में एक शून्य-चौड़ाई मैच पर विभाजित करना संभव नहीं है । लेकिन आप 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
खबरदार कि यह पहले पूंजी चरित्र से पहले किसी भी चरित्र को छोड़ देगा। 'द लॉन्गएंडविंडरऑड' का परिणाम ['लॉन्ग', 'एंड', 'विंडिंग', 'रोड']
मार्क शूलर

14
@MarcSchulder: यदि आपको उस मामले की आवश्यकता है, तो बस '[a-zA-Z][^A-Z]*'regex के रूप में उपयोग करें ।
दस्तक दें

यह upercase बिना एक ही करना संभव है?
लॉरेंट केसरो

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 काम करने से पहले स्पेस क्यों होता है? क्या यह विभाजन विधि के कारण है या यह रेगेक्स से संबंधित कुछ है?
Lax_Sam

स्प्लिट सीमांकक को किसी भी
व्हाट्सएप

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']"[A-Z][a-z']*"


+1: एबीसी काम करने के लिए सबसे पहले। मैंने अब अपना उत्तर भी अपडेट कर दिया है।
मार्क बायर्स

>>> re.findall ('[AZ] [az] *', "यह लगभग 70% अर्थव्यवस्था का है") -----> ['It', 'अर्थव्यवस्था']
ChristopheD

@ChristopheD। ओपी यह नहीं कहता कि गैर-अल्फा वर्णों के साथ कैसे व्यवहार किया जाना चाहिए।
जॉन ला रोय

1
सच है, लेकिन यह वर्तमान रेगेक्स तरीका भी dropsसभी नियमित (बस सादे अल्फा) शब्द हैं जो एक बड़े अक्षर से शुरू नहीं होते हैं। मुझे संदेह है कि यह ओपी का इरादा था।
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
अच्छा एक - यह गैर-लैटिन वर्णों के साथ भी काम करता है। यहाँ दिखाए गए रेगेक्स समाधान नहीं हैं।
एलेक्स्र फेयर

7

लुकहेड का उपयोग करें:

पायथॉन 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
फिल्टर पूरी तरह से अनावश्यक है और आप पर कब्जा समूह के साथ एक प्रत्यक्ष regex विभाजन पर कुछ भी नहीं खरीदता है: [s for s in re.compile(r"([A-Z][^A-Z]*)").split( "TheLongAndWindingRoad") if s]दे['The', 'Long', 'And', 'Winding', 'Road']
smci

1
@smci: यह उपयोग filterएक शर्त के साथ सूची समझ के समान है। क्या आपके पास इसके खिलाफ कुछ है?
गाबे

1
मुझे पता है कि इसे एक शर्त के साथ एक सूची समझ के साथ बदला जा सकता है, क्योंकि मैंने अभी उस कोड को पोस्ट किया है, फिर आपने इसे कॉपी किया। यहाँ तीन कारणों से सूची की समझ बेहतर है: क) सुगम्य मुहावरा: सूची की समझ एक अधिक पाइथोनिक मुहावरा है और filter(lambdaconditionfunc, ...)पायथन 3 में बायें की तुलना में बायें से दायें साफ पढ़ा जाता है) एक पुनरावृत्तिक filter()है। इसलिए वे पूरी तरह से समकक्ष नहीं होंगे। c) मुझे उम्मीद filter()है कि यह धीमी है
smci

4
src = 'TheLongAndWindingRoad'
glue = ' '

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

1
क्या आप इस बात का स्पष्टीकरण जोड़ सकते हैं कि यह समस्या का अच्छा समाधान क्यों है।
माटस वैटकेविसियस

मुझे माफ कर दो। मैं अंतिम चरण भूल गया हूं
user3726655

मेरे लिए संक्षिप्त, पाइथोनिक और आत्म-व्याख्यात्मक लगता है।

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_itertools60+ उपयोगी उपकरणों के साथ एक तृतीय-पक्ष पैकेज है जिसमें मूल इटर्टूलस व्यंजनों के सभी के लिए कार्यान्वयन शामिल है , जो उनके मैनुअल कार्यान्वयन को कम करता है।


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

एक वैकल्पिक तरीका का उपयोग कर enumerateऔर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

पाइथोनिक तरीका हो सकता है:

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

यूनिकोड के लिए अच्छा काम करता है, पुनः / 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 मुझे नहीं पता कि किसी ने मेरे जवाब को क्यों वोट दिया लेकिन मैं आपको इसके लिए एक नज़र रखना पसंद करूंगा। मैं आपकी प्रतिक्रिया की सराहना करूंगा।
सैमुअल नेड
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.