पायथन में, मैं एक स्ट्रिंग कैसे विभाजित करूं और विभाजक रखूं?


226

यहाँ यह समझाने का सबसे सरल तरीका है। यहाँ मैं उपयोग कर रहा हूँ:

re.split('\W', 'foo/bar spam\neggs')
-> ['foo', 'bar', 'spam', 'eggs']

यहाँ मैं क्या चाहता हूँ:

someMethod('\W', 'foo/bar spam\neggs')
-> ['foo', '/', 'bar', ' ', 'spam', '\n', 'eggs']

कारण यह है कि मैं एक स्ट्रिंग को टोकन में विभाजित करना चाहता हूं, इसे हेरफेर करता हूं, फिर इसे फिर से एक साथ वापस रख देता हूं।


3
क्या \Wखड़ा है? मैं इसे Google पर विफल कर दिया।
जोकर

8
एक गैर-शब्द चरित्र विवरण के लिए यहां देखें
रसेल

स्ट्रिंग बंटवारे के बजाय एक कच्ची बाइट स्ट्रिंग स्प्लिट के लिए, डुप्लिकेट stackoverflow.com/questions/62591863/…
लोरेंज को

जवाबों:


295
>>> re.split('(\W)', 'foo/bar spam\neggs')
['foo', '/', 'bar', ' ', 'spam', '\n', 'eggs']

22
यह अच्छा है। मुझे नहीं पता था कि re.split ने कब्जा समूहों के साथ किया था।
लॉरेंस गोंसाल्वेस

16
@ लॉरेंस : ठीक है, यह प्रलेखित है: docs.python.org/library/re.html#re.split : "पैटर्न की घटनाओं द्वारा विभाजित स्ट्रिंग। यदि कोष्ठक पर कब्जा पैटर्न में उपयोग किया जाता है, तो पैटर्न में सभी समूहों का पाठ। परिणामी सूची के हिस्से के रूप में भी लौटाया जाता है। ”
विनय साजिप

40
यह गंभीर रूप से अविकसित है। मैं 14 साल से पायथन का उपयोग कर रहा हूं और केवल यह पता चला है।
एसएमसीआई

19
क्या कोई विकल्प है ताकि समूह मैच का आउटपुट विभाजन के बाईं (या समकालिक रूप से) पर जो कुछ भी है उससे जुड़ा हुआ है? उदाहरण के लिए, क्या यह आसानी से संशोधित किया जा सकता है इसलिए आउटपुट है ['foo', '/bar', ' spam', '\neggs']?
ईली

3
@ Mr.F आप re.sub के साथ कुछ करने में सक्षम हो सकते हैं। मैं एक अंत प्रतिशत पर विभाजित करना चाहता था, इसलिए मैंने सिर्फ एक दोहरे चरित्र में विभाजित किया और फिर विभाजित हो गया, हैकी लेकिन अपने मामले के लिए काम किया: re.split('% ', re.sub('% ', '%% ', '5.000% Additional Whatnot'))->['5.000%', 'Additional Whatnot']
काइल जेम्स वॉकर

29

यदि आप न्यूलाइन पर विभाजन कर रहे हैं, तो उपयोग करें splitlines(True)

>>> 'line 1\nline 2\nline without newline'.splitlines(True)
['line 1\n', 'line 2\n', 'line without newline']

(एक सामान्य समाधान नहीं है, लेकिन किसी को यहाँ आने से इस विधि के होने का एहसास नहीं होता है।)


12

एक और नो-रेगेक्स समाधान जो पायथन 3 पर अच्छी तरह से काम करता है

# Split strings and keep separator
test_strings = ['<Hello>', 'Hi', '<Hi> <Planet>', '<', '']

def split_and_keep(s, sep):
   if not s: return [''] # consistent with string.split()

   # Find replacement character that is not used in string
   # i.e. just use the highest available character plus one
   # Note: This fails if ord(max(s)) = 0x10FFFF (ValueError)
   p=chr(ord(max(s))+1) 

   return s.replace(sep, sep+p).split(p)

for s in test_strings:
   print(split_and_keep(s, '<'))


# If the unicode limit is reached it will fail explicitly
unicode_max_char = chr(1114111)
ridiculous_string = '<Hello>'+unicode_max_char+'<World>'
print(split_and_keep(ridiculous_string, '<'))

10

यदि आपके पास केवल 1 विभाजक है, तो आप सूची बोध को नियोजित कर सकते हैं:

text = 'foo,bar,baz,qux'  
sep = ','

लागू / प्रचलित विभाजक:

result = [x+sep for x in text.split(sep)]
#['foo,', 'bar,', 'baz,', 'qux,']
# to get rid of trailing
result[-1] = result[-1].strip(sep)
#['foo,', 'bar,', 'baz,', 'qux']

result = [sep+x for x in text.split(sep)]
#[',foo', ',bar', ',baz', ',qux']
# to get rid of trailing
result[0] = result[0].strip(sep)
#['foo', ',bar', ',baz', ',qux']

विभाजक के रूप में यह स्वयं का तत्व है:

result = [u for x in text.split(sep) for u in (x, sep)]
#['foo', ',', 'bar', ',', 'baz', ',', 'qux', ',']
results = result[:-1]   # to get rid of trailing

1
आप यह भी if xसुनिश्चित करने के लिए जोड़ सकते हैं कि चंक द्वारा निर्मित splitसामग्री में कुछ सामग्री है, अर्थातresult = [x + sep for x in text.split(sep) if x]
मैं

मेरे लिए, पट्टी बहुत अधिक हटा दी गई और मुझे इसका उपयोग करना पड़ा:result = [sep+x for x in data.split(sep)] result[0] = result[0][len(sep):]
scottlittle

9

एक और उदाहरण, गैर अल्फा-न्यूमेरिक पर विभाजित करें और विभाजक रखें

import re
a = "foo,bar@candy*ice%cream"
re.split('([^a-zA-Z0-9])',a)

उत्पादन:

['foo', ',', 'bar', '@', 'candy', '*', 'ice', '%', 'cream']

व्याख्या

re.split('([^a-zA-Z0-9])',a)

() <- keep the separators
[] <- match everything in between
^a-zA-Z0-9 <-except alphabets, upper/lower and numbers.

हालांकि, जैसा कि डॉक्स कहते हैं, यह स्वीकृत उत्तर के बराबर है, मुझे इस संस्करण की पठनीयता पसंद है - भले ही \Wइसे व्यक्त करने के लिए अधिक कॉम्पैक्ट तरीका है।
एफिशिथ

3

आप एक स्ट्रिंग को रेगुलर एक्सप्रेशन के बजाय स्ट्रिंग्स की एक सरणी के साथ विभाजित कर सकते हैं, जैसे:

def tokenizeString(aString, separators):
    #separators is an array of strings that are being used to split the the string.
    #sort separators in order of descending length
    separators.sort(key=len)
    listToReturn = []
    i = 0
    while i < len(aString):
        theSeparator = ""
        for current in separators:
            if current == aString[i:i+len(current)]:
                theSeparator = current
        if theSeparator != "":
            listToReturn += [theSeparator]
            i = i + len(theSeparator)
        else:
            if listToReturn == []:
                listToReturn = [""]
            if(listToReturn[-1] in separators):
                listToReturn += [""]
            listToReturn[-1] += aString[i]
            i += 1
    return listToReturn


print(tokenizeString(aString = "\"\"\"hi\"\"\" hello + world += (1*2+3/5) '''hi'''", separators = ["'''", '+=', '+', "/", "*", "\\'", '\\"', "-=", "-", " ", '"""', "(", ")"]))

3
# This keeps all separators  in result 
##########################################################################
import re
st="%%(c+dd+e+f-1523)%%7"
sh=re.compile('[\+\-//\*\<\>\%\(\)]')

def splitStringFull(sh, st):
   ls=sh.split(st)
   lo=[]
   start=0
   for l in ls:
     if not l : continue
     k=st.find(l)
     llen=len(l)
     if k> start:
       tmp= st[start:k]
       lo.append(tmp)
       lo.append(l)
       start = k + llen
     else:
       lo.append(l)
       start =llen
   return lo
  #############################

li= splitStringFull(sh , st)
['%%(', 'c', '+', 'dd', '+', 'e', '+', 'f', '-', '1523', ')%%', '7']

3

एक आलसी और सरल समाधान

मान लें कि आपका रेगेक्स पैटर्न है split_pattern = r'(!|\?)'

पहले, आप नए विभाजक के समान कुछ वर्ण जोड़ते हैं, जैसे '[कट]'

new_string = re.sub(split_pattern, '\\1[cut]', your_string)

फिर आपने नए विभाजक को विभाजित किया, new_string.split('[cut]')


यह दृष्टिकोण चतुर है, लेकिन तब विफल हो जाएगा जब मूल स्ट्रिंग में पहले से ही [cut]कहीं शामिल है।
मैथिज्स कोइजमैन

यह बड़े पैमाने पर समस्याओं पर तेजी से हो सकता है क्योंकि यह अंततः string.split () का उपयोग करता है, अगर string.split () (जो मुझे नहीं पता है) के साथ re.split () की लागत re.sub () से अधिक है।
लॉरेंज

1

यदि कोई समूह पर कब्जा किए बिना रेगेक्स द्वारा विभाजकों को रखते हुए स्ट्रिंग को विभाजित करना चाहता है:

def finditer_with_separators(regex, s):
    matches = []
    prev_end = 0
    for match in regex.finditer(s):
        match_start = match.start()
        if (prev_end != 0 or match_start > 0) and match_start != prev_end:
            matches.append(s[prev_end:match.start()])
        matches.append(match.group())
        prev_end = match.end()
    if prev_end < len(s):
        matches.append(s[prev_end:])
    return matches

regex = re.compile(r"[\(\)]")
matches = finditer_with_separators(regex, s)

यदि कोई मानता है कि रेगेक्स को कैप्चरिंग ग्रुप में लपेट दिया गया है:

def split_with_separators(regex, s):
    matches = list(filter(None, regex.split(s)))
    return matches

regex = re.compile(r"([\(\)])")
matches = split_with_separators(regex, s)

दोनों तरीके खाली समूहों को भी हटा देंगे जो अधिकांश मामलों में बेकार और कष्टप्रद हैं।


1

यहाँ एक सरल .splitउपाय है जो रेगेक्स के बिना काम करता है।

यह सीमांकक को हटाए बिना पायथन विभाजन () के लिए एक उत्तर है , इसलिए वास्तव में मूल पोस्ट क्या पूछता है, लेकिन अन्य प्रश्न इस के लिए एक डुप्लिकेट के रूप में बंद नहीं किया गया था।

def splitkeep(s, delimiter):
    split = s.split(delimiter)
    return [substr + delimiter for substr in split[:-1]] + [split[-1]]

यादृच्छिक परीक्षण:

import random

CHARS = [".", "a", "b", "c"]
assert splitkeep("", "X") == [""]  # 0 length test
for delimiter in ('.', '..'):
    for idx in range(100000):
        length = random.randint(1, 50)
        s = "".join(random.choice(CHARS) for _ in range(length))
        assert "".join(splitkeep(s, delimiter)) == s

रेगेक्स को गति के कारणों के लिए बड़े पैमाने पर समस्याओं से बचा जाना चाहिए, यही कारण है कि यह एक अच्छा संकेत है।
लॉरेंज

0

मेरे पास एक समान मुद्दा था एक फ़ाइल पथ को विभाजित करने की कोशिश करना और एक सरल उत्तर खोजने के लिए संघर्ष करना। यह मेरे लिए काम करता है और इसमें विभाजित पाठ में सीमांकक को स्थानापन्न करने में शामिल नहीं होता है:

my_path = 'folder1/folder2/folder3/file1'

import re

re.findall('[^/]+/|[^/]+', my_path)

रिटर्न:

['folder1/', 'folder2/', 'folder3/', 'file1']


इसका उपयोग करके इसे थोड़ा सरल बनाया जा सकता है: re.findall('[^/]+/?', my_path)(जैसे ?दो विकल्पों के साथ प्रदान करने के बजाय अनुगामी स्लैश को वैकल्पिक बनाना |
मैथिज्स कोइजमैन

0

मुझे यह जनरेटर आधारित दृष्टिकोण अधिक संतोषजनक लगा:

def split_keep(string, sep):
    """Usage:
    >>> list(split_keep("a.b.c.d", "."))
    ['a.', 'b.', 'c.', 'd']
    """
    start = 0
    while True:
        end = string.find(sep, start) + 1
        if end == 0:
            break
        yield string[start:end]
        start = end
    yield string[start:]

यह सही रेगेक्स का पता लगाने की आवश्यकता से बचता है, जबकि सिद्धांत में काफी सस्ता होना चाहिए। यह नई स्ट्रिंग ऑब्जेक्ट्स नहीं बनाता है और कुशल खोज विधि में अधिकांश पुनरावृत्ति कार्य को दर्शाता है।

... और पायथन 3.8 में यह जितना छोटा हो सकता है:

def split_keep(string, sep):
    start = 0
    while (end := string.find(sep, start) + 1) > 0:
        yield string[start:end]
        start = end
    yield string[start:]

0
  1. सभी के seperator: (\W)साथ बदलेंseperator + new_seperator: (\W;)

  2. द्वारा विभाजित new_seperator: (;)

def split_and_keep(seperator, s):
  return re.split(';', re.sub(seperator, lambda match: match.group() + ';', s))

print('\W', 'foo/bar spam\neggs')
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.