มีเวอร์ชันกำเนิดของ `string.split ()` ใน Python หรือไม่


113

string.split()ส่งคืนอินสแตนซ์รายการ มีรุ่นที่ส่งคืนเครื่องกำเนิดไฟฟ้าแทนหรือไม่? มีเหตุผลใดบ้างที่จะมีรุ่นเครื่องกำเนิดไฟฟ้า?


3
คำถามนี้อาจเกี่ยวข้อง
Björn Pollex

1
เหตุผลก็คือมันยากมากที่จะนึกถึงกรณีที่มีประโยชน์ ทำไมคุณถึงต้องการสิ่งนี้?
Glenn Maynard

10
@Glenn: เมื่อเร็ว ๆ นี้ฉันเห็นคำถามเกี่ยวกับการแยกสตริงยาวเป็นชิ้น ๆ ของ n คำ หนึ่งในวิธีแก้ปัญหาsplitคือสตริงแล้วส่งคืนเครื่องกำเนิดไฟฟ้าที่ทำงานกับผลลัพธ์ของsplit. นั่นทำให้ฉันคิดว่ามีวิธีsplitคืนเครื่องกำเนิดไฟฟ้าเพื่อเริ่มต้นด้วยหรือไม่
Manoj Govindan

5
มีการอภิปรายที่เกี่ยวข้องในตัวติดตามปัญหา Python: bugs.python.org/issue17343
saffsd

@GlennMaynard มันมีประโยชน์สำหรับการแยกสตริง / ไฟล์เปล่าที่มีขนาดใหญ่มาก แต่ใคร ๆ ก็สามารถเขียนตัวแยกวิเคราะห์เครื่องกำเนิดไฟฟ้าด้วยตัวเองได้อย่างง่ายดายโดยใช้ DFA ที่ชงเองและให้ผลผลิต
Dmitry Ponyatov

คำตอบ:


77

มีความเป็นไปได้สูงที่re.finditerจะใช้หน่วยความจำเหนือศีรษะที่ค่อนข้างน้อย

def split_iter(string):
    return (x.group(0) for x in re.finditer(r"[A-Za-z']+", string))

การสาธิต:

>>> list( split_iter("A programmer's RegEx test.") )
['A', "programmer's", 'RegEx', 'test']

แก้ไข:ฉันเพิ่งยืนยันว่าสิ่งนี้ใช้หน่วยความจำคงที่ใน python 3.2.1 โดยถือว่าวิธีการทดสอบของฉันถูกต้อง ฉันสร้างสตริงที่มีขนาดใหญ่มาก (1GB หรือมากกว่านั้น) จากนั้นวนซ้ำผ่านการวนซ้ำด้วยการforวนซ้ำ (ไม่ใช่ความเข้าใจรายการซึ่งจะสร้างหน่วยความจำเพิ่มเติม) สิ่งนี้ไม่ได้ส่งผลให้หน่วยความจำเติบโตขึ้นอย่างเห็นได้ชัด (นั่นคือถ้ามีการเติบโตของหน่วยความจำก็จะน้อยกว่าสตริง 1GB มาก)


5
ยอดเยี่ยม! ฉันลืมเรื่อง finditer ไปแล้ว หากมีใครสนใจที่จะทำอะไรบางอย่างเช่น Splitlines ฉันขอแนะนำให้ใช้ RE: '(. * \ n |. + $)' str.splitlines นี้จะตัดบรรทัดใหม่ของการฝึกอบรมออกไป (สิ่งที่ฉันไม่ชอบจริงๆ ... ); หากคุณต้องการจำลองพฤติกรรมส่วนนั้นคุณสามารถใช้การจัดกลุ่ม: (m.group (2) หรือ m.group (3) สำหรับ m ใน re.finditer ('((. *) \ n | (. +) $) ', s)) PS: ฉันเดาว่าไม่จำเป็นต้องใช้แผงด้านนอกใน RE ฉันรู้สึกไม่สบายใจที่จะใช้ | ไม่มี paren: P
allyourcode

3
แล้วประสิทธิภาพล่ะ? การจับคู่ซ้ำควรช้ากว่าการค้นหาทั่วไป
anatoly techtonik

1
คุณจะเขียนฟังก์ชัน Split_iter นี้ใหม่ให้ทำงานa_string.split("delimiter")อย่างไร
Moberg

Split ยอมรับนิพจน์ทั่วไปอยู่แล้วดังนั้นจึงไม่เร็วกว่าจริงๆหากคุณต้องการใช้ค่าที่ส่งคืนในรูปแบบถัดไปดูคำตอบของฉันที่ด้านล่าง ...
Veltzer Doron

str.split()ไม่ยอมรับนิพจน์ทั่วไปที่re.split()คุณกำลังนึกถึง ...
alexis

17

วิธีที่มีประสิทธิภาพที่สุดที่ฉันคิดได้คือการเขียนโดยใช้offsetพารามิเตอร์ของstr.find()วิธีการ วิธีนี้หลีกเลี่ยงการใช้หน่วยความจำจำนวนมากและอาศัยค่าใช้จ่ายของ regexp เมื่อไม่จำเป็น

[แก้ไข 2016-8-2: อัปเดตสิ่งนี้เพื่อรองรับตัวคั่นนิพจน์ทั่วไป]

def isplit(source, sep=None, regex=False):
    """
    generator version of str.split()

    :param source:
        source string (unicode or bytes)

    :param sep:
        separator to split on.

    :param regex:
        if True, will treat sep as regular expression.

    :returns:
        generator yielding elements of string.
    """
    if sep is None:
        # mimic default python behavior
        source = source.strip()
        sep = "\\s+"
        if isinstance(source, bytes):
            sep = sep.encode("ascii")
        regex = True
    if regex:
        # version using re.finditer()
        if not hasattr(sep, "finditer"):
            sep = re.compile(sep)
        start = 0
        for m in sep.finditer(source):
            idx = m.start()
            assert idx >= start
            yield source[start:idx]
            start = m.end()
        yield source[start:]
    else:
        # version using str.find(), less overhead than re.finditer()
        sepsize = len(sep)
        start = 0
        while True:
            idx = source.find(sep, start)
            if idx == -1:
                yield source[start:]
                return
            yield source[start:idx]
            start = idx + sepsize

นี้สามารถใช้ได้อย่างที่คุณต้องการ ...

>>> print list(isplit("abcb","b"))
['a','c','']

แม้ว่าจะมีการค้นหาต้นทุนเล็กน้อยภายในสตริงทุกครั้งที่ดำเนินการ find () หรือการแบ่งส่วน แต่ก็ควรน้อยที่สุดเนื่องจากสตริงจะแสดงเป็นอาร์เรย์ที่จำเป็นในหน่วยความจำ


10

นี่เป็นเวอร์ชันของเครื่องกำเนิดไฟฟ้าที่split()ใช้งานผ่านre.search()ที่ไม่มีปัญหาในการจัดสรรสตริงย่อยมากเกินไป

import re

def itersplit(s, sep=None):
    exp = re.compile(r'\s+' if sep is None else re.escape(sep))
    pos = 0
    while True:
        m = exp.search(s, pos)
        if not m:
            if pos < len(s) or sep is not None:
                yield s[pos:]
            break
        if pos < m.start() or sep is not None:
            yield s[pos:m.start()]
        pos = m.end()


sample1 = "Good evening, world!"
sample2 = " Good evening, world! "
sample3 = "brackets][all][][over][here"
sample4 = "][brackets][all][][over][here]["

assert list(itersplit(sample1)) == sample1.split()
assert list(itersplit(sample2)) == sample2.split()
assert list(itersplit(sample3, '][')) == sample3.split('][')
assert list(itersplit(sample4, '][')) == sample4.split('][')

แก้ไข:แก้ไขการจัดการช่องว่างโดยรอบหากไม่มีการระบุตัวคั่น


12
ทำไมถึงดีไปกว่านี้re.finditer?
Erik Kaplun

@ErikKaplun เนื่องจากตรรกะ regex สำหรับรายการอาจซับซ้อนกว่าตัวคั่น ในกรณีของฉันฉันต้องการประมวลผลทีละบรรทัดดังนั้นฉันจึงสามารถรายงานกลับได้หากไม่สามารถจับคู่บรรทัดได้
rovyko

9

ทำการทดสอบประสิทธิภาพตามวิธีการต่างๆที่เสนอ (ฉันจะไม่ทำซ้ำที่นี่) ผลลัพธ์บางส่วน:

  • str.split (ค่าเริ่มต้น = 0.3461570239996945
  • การค้นหาด้วยตนเอง (ตามอักขระ) (หนึ่งในคำตอบของ Dave Webb) = 0.8260340550004912
  • re.finditer (คำตอบของ Ninjagecko) = 0.698872097000276
  • str.find (หนึ่งในคำตอบของ Eli Collins) = 0.7230395330007013
  • itertools.takewhile (คำตอบของ Ignacio Vazquez-Abrams) = 2.023023967998597
  • str.split(..., maxsplit=1) การเรียกซ้ำ = N / A †

†คำตอบการเรียกซ้ำ ( string.splitมีmaxsplit = 1) ไม่สามารถดำเนินการให้เสร็จสิ้นในเวลาอันสมควรเนื่องจากstring.splitความเร็วของพวกเขาอาจทำงานได้ดีขึ้นกับสตริงที่สั้นกว่า แต่ฉันไม่เห็นกรณีการใช้งานสำหรับสตริงสั้น ๆ ซึ่งหน่วยความจำไม่ใช่ปัญหาอยู่ดี

ทดสอบโดยใช้timeitเมื่อ:

the_text = "100 " * 9999 + "100"

def test_function( method ):
    def fn( ):
        total = 0

        for x in method( the_text ):
            total += int( x )

        return total

    return fn

ทำให้เกิดคำถามอีกว่าทำไมถึงstring.splitเร็วขึ้นมากทั้งๆที่ใช้หน่วยความจำ


2
เนื่องจากหน่วยความจำช้ากว่า cpu และในกรณีนี้รายการจะถูกโหลดโดยชิ้นส่วนที่อื่น ๆ ทั้งหมดจะถูกโหลดโดยองค์ประกอบ ในบันทึกเดียวกันนักวิชาการหลายคนจะบอกคุณว่ารายการที่เชื่อมโยงนั้นเร็วกว่าและมีความซับซ้อนน้อยกว่าในขณะที่คอมพิวเตอร์ของคุณมักจะเร็วกว่าด้วยอาร์เรย์ซึ่งจะช่วยให้เพิ่มประสิทธิภาพได้ง่ายขึ้น คุณไม่สามารถถือว่าตัวเลือกนั้นเร็วกว่าตัวเลือกอื่นได้ทดสอบ! +1 สำหรับการทดสอบ
Benoît P

ปัญหาเกิดขึ้นในขั้นตอนถัดไปของห่วงโซ่การประมวลผล หากคุณต้องการค้นหาชิ้นส่วนที่เฉพาะเจาะจงและเพิกเฉยต่อส่วนที่เหลือเมื่อคุณพบแล้วคุณมีเหตุผลที่จะใช้ตัวแยกตามเครื่องกำเนิดไฟฟ้าแทนโซลูชันในตัว
jgomo3

6

นี่คือการนำไปใช้ของฉันซึ่งเร็วกว่าและสมบูรณ์กว่าคำตอบอื่น ๆ ที่นี่มาก มีฟังก์ชั่นย่อย 4 ฟังก์ชันแยกกันสำหรับกรณีต่างๆ

ฉันจะคัดลอก docstring ของstr_splitฟังก์ชันหลัก:


str_split(s, *delims, empty=None)

แยกสตริงsตามส่วนที่เหลือของอาร์กิวเมนต์โดยอาจละเว้นส่วนที่ว่าง ( emptyอาร์กิวเมนต์คำหลักเป็นผู้รับผิดชอบในส่วนนั้น) นี่คือฟังก์ชันเครื่องกำเนิดไฟฟ้า

เมื่อมีตัวคั่นเพียงตัวเดียวสตริงจะถูกแยกออกจากกัน emptyเป็นแล้วTrueโดยค่าเริ่มต้น

str_split('[]aaa[][]bb[c', '[]')
    -> '', 'aaa', '', 'bb[c'
str_split('[]aaa[][]bb[c', '[]', empty=False)
    -> 'aaa', 'bb[c'

เมื่อมีการระบุตัวคั่นหลายตัวสตริงจะถูกแบ่งตามลำดับที่ยาวที่สุดที่เป็นไปได้ของตัวคั่นเหล่านั้นตามค่าเริ่มต้นหรือหากemptyตั้งค่าเป็น Trueสตริงว่างระหว่างตัวคั่นจะรวมอยู่ด้วย โปรดทราบว่าตัวคั่นในกรณีนี้ต้องเป็นอักขระเดี่ยวเท่านั้น

str_split('aaa, bb : c;', ' ', ',', ':', ';')
    -> 'aaa', 'bb', 'c'
str_split('aaa, bb : c;', *' ,:;', empty=True)
    -> 'aaa', '', 'bb', '', '', 'c', ''

เมื่อไม่มีการระบุตัวคั่นจะstring.whitespaceถูกใช้ดังนั้นเอฟเฟกต์จะเหมือนกับstr.split()ยกเว้นฟังก์ชันนี้เป็นตัวสร้าง

str_split('aaa\\t  bb c \\n')
    -> 'aaa', 'bb', 'c'

import string

def _str_split_chars(s, delims):
    "Split the string `s` by characters contained in `delims`, including the \
    empty parts between two consecutive delimiters"
    start = 0
    for i, c in enumerate(s):
        if c in delims:
            yield s[start:i]
            start = i+1
    yield s[start:]

def _str_split_chars_ne(s, delims):
    "Split the string `s` by longest possible sequences of characters \
    contained in `delims`"
    start = 0
    in_s = False
    for i, c in enumerate(s):
        if c in delims:
            if in_s:
                yield s[start:i]
                in_s = False
        else:
            if not in_s:
                in_s = True
                start = i
    if in_s:
        yield s[start:]


def _str_split_word(s, delim):
    "Split the string `s` by the string `delim`"
    dlen = len(delim)
    start = 0
    try:
        while True:
            i = s.index(delim, start)
            yield s[start:i]
            start = i+dlen
    except ValueError:
        pass
    yield s[start:]

def _str_split_word_ne(s, delim):
    "Split the string `s` by the string `delim`, not including empty parts \
    between two consecutive delimiters"
    dlen = len(delim)
    start = 0
    try:
        while True:
            i = s.index(delim, start)
            if start!=i:
                yield s[start:i]
            start = i+dlen
    except ValueError:
        pass
    if start<len(s):
        yield s[start:]


def str_split(s, *delims, empty=None):
    """\
Split the string `s` by the rest of the arguments, possibly omitting
empty parts (`empty` keyword argument is responsible for that).
This is a generator function.

When only one delimiter is supplied, the string is simply split by it.
`empty` is then `True` by default.
    str_split('[]aaa[][]bb[c', '[]')
        -> '', 'aaa', '', 'bb[c'
    str_split('[]aaa[][]bb[c', '[]', empty=False)
        -> 'aaa', 'bb[c'

When multiple delimiters are supplied, the string is split by longest
possible sequences of those delimiters by default, or, if `empty` is set to
`True`, empty strings between the delimiters are also included. Note that
the delimiters in this case may only be single characters.
    str_split('aaa, bb : c;', ' ', ',', ':', ';')
        -> 'aaa', 'bb', 'c'
    str_split('aaa, bb : c;', *' ,:;', empty=True)
        -> 'aaa', '', 'bb', '', '', 'c', ''

When no delimiters are supplied, `string.whitespace` is used, so the effect
is the same as `str.split()`, except this function is a generator.
    str_split('aaa\\t  bb c \\n')
        -> 'aaa', 'bb', 'c'
"""
    if len(delims)==1:
        f = _str_split_word if empty is None or empty else _str_split_word_ne
        return f(s, delims[0])
    if len(delims)==0:
        delims = string.whitespace
    delims = set(delims) if len(delims)>=4 else ''.join(delims)
    if any(len(d)>1 for d in delims):
        raise ValueError("Only 1-character multiple delimiters are supported")
    f = _str_split_chars if empty else _str_split_chars_ne
    return f(s, delims)

ฟังก์ชั่นนี้ใช้งานได้ใน Python 3 และสามารถใช้การแก้ไขที่ง่าย แต่ค่อนข้างน่าเกลียดเพื่อให้ใช้งานได้ทั้ง 2 และ 3 เวอร์ชัน บรรทัดแรกของฟังก์ชันควรเปลี่ยนเป็น:

def str_split(s, *delims, **kwargs):
    """...docstring..."""
    empty = kwargs.get('empty')

3

ไม่มี itertools.takewhile()แต่มันควรจะเพียงพอที่ง่ายต่อการเขียนโดยใช้

แก้ไข:

การใช้งานที่ง่ายและหักครึ่ง:

import itertools
import string

def isplitwords(s):
  i = iter(s)
  while True:
    r = []
    for c in itertools.takewhile(lambda x: not x in string.whitespace, i):
      r.append(c)
    else:
      if r:
        yield ''.join(r)
        continue
      else:
        raise StopIteration()

@Ignacio: ตัวอย่างในเอกสารใช้รายการจำนวนเต็มเพื่อแสดงการใช้takeWhile. อะไรคือสิ่งที่ดีpredicateสำหรับการแยกสตริงออกเป็นคำ (ค่าเริ่มต้นsplit) โดยใช้takeWhile()?
Manoj Govindan

string.whitespaceมองหาการปรากฏใน
Ignacio Vazquez-Abrams

ตัวคั่นสามารถมีได้หลายอักขระ'abc<def<>ghi<><>lmn'.split('<>') == ['abc<def', 'ghi', '', 'lmn']
kenny ™

@Ignacio: คุณสามารถเพิ่มตัวอย่างคำตอบของคุณได้หรือไม่?
Manoj Govindan

1
เขียนง่าย แต่มีลำดับความสำคัญช้ากว่ามาก นี่คือการดำเนินการที่ควรนำไปใช้ในโค้ดเนทีฟ
Glenn Maynard

3

ฉันไม่เห็นประโยชน์ที่ชัดเจนสำหรับเวอร์ชันเครื่องกำเนิดไฟฟ้าของsplit()ไฟล์. วัตถุเครื่องกำเนิดไฟฟ้าจะต้องมีสตริงทั้งหมดเพื่อวนซ้ำดังนั้นคุณจะไม่บันทึกหน่วยความจำใด ๆ โดยมีเครื่องกำเนิดไฟฟ้า

หากคุณต้องการเขียนมันจะค่อนข้างง่ายแม้ว่า:

import string

def gsplit(s,sep=string.whitespace):
    word = []

    for c in s:
        if c in sep:
            if word:
                yield "".join(word)
                word = []
        else:
            word.append(c)

    if word:
        yield "".join(word)

3
คุณลดหน่วยความจำที่ใช้ลงครึ่งหนึ่งโดยไม่ต้องจัดเก็บสำเนาที่สองของสตริงในแต่ละส่วนที่เป็นผลลัพธ์รวมทั้งอาร์เรย์และค่าใช้จ่ายของอ็อบเจ็กต์ (ซึ่งโดยทั่วไปจะมากกว่าสตริงเอง) โดยทั่วไปไม่สำคัญ (หากคุณกำลังแยกสตริงที่มีขนาดใหญ่มากจนมีความสำคัญคุณอาจทำอะไรผิดพลาด) และแม้แต่การใช้งานเครื่องกำเนิด C แบบเนทีฟก็มักจะช้ากว่าการทำทั้งหมดในครั้งเดียว
Glenn Maynard

@Glenn Maynard - ฉันเพิ่งรู้ตัว ฉันด้วยเหตุผลบางอย่าง แต่เดิมเครื่องกำเนิดไฟฟ้าจะจัดเก็บสำเนาของสตริงแทนที่จะเป็นข้อมูลอ้างอิง ตรวจสอบอย่างรวดเร็วด้วยid()ทำให้ฉันถูกต้อง และเห็นได้ชัดว่าสตริงไม่เปลี่ยนรูปคุณไม่จำเป็นต้องกังวลว่าจะมีคนเปลี่ยนสตริงเดิมในขณะที่คุณทำซ้ำ
Dave Webb

6
ประเด็นหลักในการใช้เครื่องกำเนิดไฟฟ้าไม่ใช่การใช้หน่วยความจำ แต่คุณสามารถช่วยตัวเองได้โดยต้องแยกสตริงทั้งหมดหากคุณต้องการออกก่อน? (นั่นไม่ใช่ความคิดเห็นเกี่ยวกับโซลูชันเฉพาะของคุณฉันรู้สึกประหลาดใจกับการสนทนาเกี่ยวกับความจำ)
Scott Griffiths

@Scott: มันยากที่จะนึกถึงกรณีที่ชนะจริงๆ - โดยที่ 1: คุณต้องการหยุดแยกส่วน 2: คุณไม่รู้ว่าคุณกำลังแยกคำล่วงหน้ากี่คำ 3: คุณมี สตริงที่ใหญ่พอที่จะให้มันมีความสำคัญและ 4: คุณหยุดเร็วพอที่จะชนะอย่างมีนัยสำคัญมากกว่า str.split นั่นเป็นชุดเงื่อนไขที่แคบมาก
Glenn Maynard

4
คุณจะได้รับประโยชน์สูงกว่ามากหากสตริงของคุณสร้างขึ้นอย่างเกียจคร้านเช่นกัน (เช่นจากการรับส่งข้อมูลเครือข่ายหรือการอ่านไฟล์)
Lie Ryan

3

ฉันเขียนคำตอบของ @ ninjagecko เวอร์ชันที่ทำงานเหมือน string.split มากกว่า (เช่นช่องว่างคั่นด้วยค่าเริ่มต้นและคุณสามารถระบุตัวคั่นได้)

def isplit(string, delimiter = None):
    """Like string.split but returns an iterator (lazy)

    Multiple character delimters are not handled.
    """

    if delimiter is None:
        # Whitespace delimited by default
        delim = r"\s"

    elif len(delimiter) != 1:
        raise ValueError("Can only handle single character delimiters",
                        delimiter)

    else:
        # Escape, incase it's "\", "*" etc.
        delim = re.escape(delimiter)

    return (x.group(0) for x in re.finditer(r"[^{}]+".format(delim), string))

นี่คือการทดสอบที่ฉันใช้ (ทั้งใน python 3 และ python 2):

# Wrapper to make it a list
def helper(*args,  **kwargs):
    return list(isplit(*args, **kwargs))

# Normal delimiters
assert helper("1,2,3", ",") == ["1", "2", "3"]
assert helper("1;2;3,", ";") == ["1", "2", "3,"]
assert helper("1;2 ;3,  ", ";") == ["1", "2 ", "3,  "]

# Whitespace
assert helper("1 2 3") == ["1", "2", "3"]
assert helper("1\t2\t3") == ["1", "2", "3"]
assert helper("1\t2 \t3") == ["1", "2", "3"]
assert helper("1\n2\n3") == ["1", "2", "3"]

# Surrounding whitespace dropped
assert helper(" 1 2  3  ") == ["1", "2", "3"]

# Regex special characters
assert helper(r"1\2\3", "\\") == ["1", "2", "3"]
assert helper(r"1*2*3", "*") == ["1", "2", "3"]

# No multi-char delimiters allowed
try:
    helper(r"1,.2,.3", ",.")
    assert False
except ValueError:
    pass

โมดูล regex ของ python บอกว่า"สิ่งที่ถูกต้อง"สำหรับช่องว่างยูนิโคด แต่ฉันยังไม่ได้ทดสอบจริงๆ

ยังมีเป็นส่วนสำคัญ


3

หากคุณต้องการอ่านตัววนซ้ำ (เช่นเดียวกับการส่งกลับ ) ลองสิ่งนี้:

import itertools as it

def iter_split(string, sep=None):
    sep = sep or ' '
    groups = it.groupby(string, lambda s: s != sep)
    return (''.join(g) for k, g in groups if k)

การใช้งาน

>>> list(iter_split(iter("Good evening, world!")))
['Good', 'evening,', 'world!']

3

more_itertools.split_atเสนออะนาล็อกstr.splitสำหรับตัวทำซ้ำ

>>> import more_itertools as mit


>>> list(mit.split_at("abcdcba", lambda x: x == "b"))
[['a'], ['c', 'd', 'c'], ['a']]

>>> "abcdcba".split("b")
['a', 'cdc', 'a']

more_itertools เป็นแพ็คเกจของบุคคลที่สาม


1
โปรดสังเกตว่า more_itertools.split_at () ยังคงใช้รายการที่จัดสรรใหม่ในการโทรแต่ละครั้งดังนั้นแม้ว่าจะส่งคืนตัววนซ้ำ แต่ก็ไม่บรรลุข้อกำหนดหน่วยความจำคงที่ ดังนั้นขึ้นอยู่กับสาเหตุที่คุณต้องการให้ตัววนซ้ำเริ่มต้นสิ่งนี้อาจเป็นประโยชน์หรือไม่ก็ได้
jcater

@jcater จุดดี. ค่ากลางเป็น buffered แน่นอนเป็นรายการย่อยภายใน iterator ตามของการดำเนินงาน เราสามารถปรับแหล่งที่มาเพื่อแทนที่รายการด้วยตัววนซ้ำผนวกitertools.chainและประเมินผลลัพธ์โดยใช้ความเข้าใจรายการ ขึ้นอยู่กับความต้องการและคำขอฉันสามารถโพสต์ตัวอย่าง
pylang

2

ฉันต้องการแสดงวิธีใช้โซลูชัน find_iter เพื่อส่งคืนเครื่องกำเนิดไฟฟ้าสำหรับตัวคั่นที่กำหนดจากนั้นใช้สูตรคู่จาก itertools เพื่อสร้างการทำซ้ำถัดไปก่อนหน้านี้ซึ่งจะได้คำจริงเช่นเดียวกับวิธีการแยกดั้งเดิม


from more_itertools import pairwise
import re

string = "dasdha hasud hasuid hsuia dhsuai dhasiu dhaui d"
delimiter = " "
# split according to the given delimiter including segments beginning at the beginning and ending at the end
for prev, curr in pairwise(re.finditer("^|[{0}]+|$".format(delimiter), string)):
    print(string[prev.end(): curr.start()])

บันทึก:

  1. ฉันใช้ prev & curr แทน prev & next เพราะการแทนที่ next ใน python นั้นเป็นความคิดที่แย่มาก
  2. ซึ่งค่อนข้างมีประสิทธิภาพ

1

วิธีที่โง่ที่สุดโดยไม่ต้อง regex / itertools:

def isplit(text, split='\n'):
    while text != '':
        end = text.find(split)

        if end == -1:
            yield text
            text = ''
        else:
            yield text[:end]
            text = text[end + 1:]

0
def split_generator(f,s):
    """
    f is a string, s is the substring we split on.
    This produces a generator rather than a possibly
    memory intensive list. 
    """
    i=0
    j=0
    while j<len(f):
        if i>=len(f):
            yield f[j:]
            j=i
        elif f[i] != s:
            i=i+1
        else:
            yield [f[j:i]]
            j=i+1
            i=i+1

ทำไมคุณให้ผล[f[j:i]]และไม่f[j:i]?
Moberg

0

นี่คือคำตอบง่ายๆ

def gen_str(some_string, sep):
    j=0
    guard = len(some_string)-1
    for i,s in enumerate(some_string):
        if s == sep:
           yield some_string[j:i]
           j=i+1
        elif i!=guard:
           continue
        else:
           yield some_string[j:]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.