string.split()
목록 인스턴스를 반환 합니다. 대신 생성기 를 반환하는 버전이 있습니까? 생성기 버전을 사용하지 않는 이유가 있습니까?
split
문자열을 반환 한 다음 split
. 그로 split
인해 발전기를 반환하는 방법이 있는지 생각 했습니다.
string.split()
목록 인스턴스를 반환 합니다. 대신 생성기 를 반환하는 버전이 있습니까? 생성기 버전을 사용하지 않는 이유가 있습니까?
split
문자열을 반환 한 다음 split
. 그로 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 문자열보다 훨씬 적음).
a_string.split("delimiter")
하시겠습니까?
str.split()
정규 표현식을 허용하지 않는, 그건 re.split()
당신의있는 거 생각 ...
내가 생각할 수있는 가장 효율적인 방법 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('][')
편집 : 구분 문자가 제공되지 않은 경우 주변 공백 처리를 수정했습니다.
re.finditer
?
제안 된 다양한 방법에 대한 성능 테스트를 수행했습니다 (여기서는 반복하지 않겠습니다). 몇 가지 결과 :
str.split
(기본값 = 0.3461570239996945re.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()
takeWhile
. 무엇을 사용하여 predicate
문자열을 단어 (기본값 split
) 로 분할 하는 것이 takeWhile()
좋을까요?
string.whitespace
.
'abc<def<>ghi<><>lmn'.split('<>') == ['abc<def', 'ghi', '', 'lmn']
생성기 버전의 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)
id()
바로 내를 넣어. 그리고 분명히 문자열은 불변하기 때문에 반복하는 동안 누군가가 원래 문자열을 변경하는 것에 대해 걱정할 필요가 없습니다.
나는 string.split (즉, 기본적으로 공백으로 구분되고 구분 기호를 지정할 수 있음)처럼 동작하는 @ninjagecko의 답변 버전을 작성했습니다.
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
파이썬의 정규식 모듈은 유니 코드 공백에 대해 "올바른 일" 을 한다고 말하지만 실제로 테스트하지는 않았습니다.
요점 으로도 사용할 수 있습니다 .
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
타사 패키지입니다.
find_iter 솔루션을 사용하여 지정된 구분 기호에 대한 생성기를 반환 한 다음 itertools의 pairwise 레시피를 사용하여 원래 split 메소드에서와 같이 실제 단어를 가져올 이전 다음 반복을 빌드하는 방법을 보여주고 싶었습니다.
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()])
노트:
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]
않습니까?
여기에 간단한 응답이 있습니다
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:]