string.split()
목록 인스턴스를 반환 합니다. 대신 생성기 를 반환하는 버전이 있습니까? 생성기 버전을 사용하지 않는 이유가 있습니까?
답변
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']
편집 : 내 테스트 방법이 정확하다고 가정하고 파이썬 3.2.1에서 일정한 메모리를 차지한다는 것을 방금 확인했습니다. 나는 매우 큰 크기 (1GB 정도)의 문자열을 만든 다음 for
루프 (추가 메모리를 생성 할 수있는 목록 이해가 아님) 를 사용하여 반복 가능 항목을 반복했습니다 . 이로 인해 메모리가 눈에 띄게 증가하지는 않았습니다 (즉, 메모리가 증가하면 1GB 문자열보다 훨씬 적음).
답변
내가 생각할 수있는 가장 효율적인 방법 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 () 또는 슬라이싱이 수행 될 때마다 문자열 내에서 검색하는 데 약간의 비용이 발생하지만 문자열은 메모리에서 연속적인 배열로 표현되기 때문에 최소화되어야합니다.
답변
이것은 너무 많은 하위 문자열을 할당하는 문제가없는 split()
via 구현의 생성기 버전입니다 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('][')
편집 : 구분 문자가 제공되지 않은 경우 주변 공백 처리를 수정했습니다.
답변
제안 된 다양한 방법에 대한 성능 테스트를 수행했습니다 (여기서는 반복하지 않겠습니다). 몇 가지 결과 :
str.split
(기본값 = 0.3461570239996945- 수동 검색 (문자 별) (Dave Webb의 답변 중 하나) = 0.8260340550004912
re.finditer
(ninjagecko의 답변) = 0.698872097000276str.find
(Eli Collins의 답변 중 하나) = 0.7230395330007013itertools.takewhile
(Ignacio Vazquez-Abrams의 답변) = 2.023023967998597str.split(..., maxsplit=1)
재귀 = N / A †
† (재귀 답변 string.split
과 함께 maxsplit = 1
, 적절한 시간에 완료하는 데 실패) 지정된 string.split
S가 짧은 문자열에 더 잘 작동 할 수 있습니다 속도를하지만, 메모리 어쨌든 문제가되지 않는 경우 그때 나는 짧은 문자열에 대한 사용 사례를 볼 수 없습니다.
다음을 사용하여 테스트 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
메모리 사용량에도 불구하고 왜 훨씬 더 빠른지에 대한 또 다른 질문을 제기 합니다.
답변
여기에 다른 답변보다 훨씬 빠르고 완벽하게 구현 된 구현이 있습니다. 다른 경우에 대해 4 개의 별도 하위 기능이 있습니다.
주 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')
답변
아니요,하지만 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()
답변
생성기 버전의 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)
