programing

문자열의 줄 위에 반복합니다.

linuxpc 2023. 8. 14. 22:32
반응형

문자열의 줄 위에 반복합니다.

다음과 같이 정의된 다중 줄 문자열이 있습니다.

foo = """
this is 
a multi-line string.
"""

제가 쓰고 있는 파서의 테스트 입력으로 사용한 문자열입니다.는 서파함다수니다합신음을 받습니다.file그 위에서 - 입로사고하그반다니복위합서에용력으객를체▁-다니▁as반.그것은 또한 그것을 부릅니다.next()직접 줄을 건너뛸 수 있는 방법이므로 반복자가 아니라 반복자가 입력으로 필요합니다.저는 그 문자열의 개별 줄에 걸쳐 반복하는 반복기가 필요합니다.file의 줄 .물론 저는 이렇게 할 수 있습니다.

lineiterator = iter(foo.splitlines())

이것을 하는 더 직접적인 방법이 방법이 있습니까?이 시나리오에서 문자열은 분할을 위해 한 번 이동한 다음 파서를 통해 다시 이동해야 합니다.제 테스트 케이스에서는 상관없습니다, 거기는 줄이 매우 짧기 때문에 그냥 궁금해서 물어본 것입니다.Python은 그러한 것들을 위해 매우 유용하고 효율적인 내장을 가지고 있지만, 저는 이 필요에 맞는 것을 찾을 수 없었습니다.

세 가지 가능성이 있습니다.

foo = """
this is 
a multi-line string.
"""

def f1(foo=foo): return iter(foo.splitlines())

def f2(foo=foo):
    retval = ''
    for char in foo:
        retval += char if not char == '\n' else ''
        if char == '\n':
            yield retval
            retval = ''
    if retval:
        yield retval

def f3(foo=foo):
    prevnl = -1
    while True:
      nextnl = foo.find('\n', prevnl + 1)
      if nextnl < 0: break
      yield foo[prevnl + 1:nextnl]
      prevnl = nextnl

if __name__ == '__main__':
  for f in f1, f2, f3:
    print list(f())

이를 주 스크립트로 실행하면 세 가지 기능이 동일함을 확인할 수 있습니다.와 함께timeit a (a)* 100위해서foo보다 정확한 측정을 위해 상당한 문자열을 얻는 방법):

$ python -mtimeit -s'import asp' 'list(asp.f3())'
1000 loops, best of 3: 370 usec per loop
$ python -mtimeit -s'import asp' 'list(asp.f2())'
1000 loops, best of 3: 1.36 msec per loop
$ python -mtimeit -s'import asp' 'list(asp.f1())'
10000 loops, best of 3: 61.5 usec per loop

참고로 우리는 그것이 필요합니다.list()반복자들이 단지 구축된 것이 아니라 횡단적인지 확인하기 위한 호출.

도 않습니다: IOW를 더 빠릅니다. 제가 시도한 것보다 6배 더 빠릅니다.find낮은 수준의 접근 방식보다 4배 더 빠른 통화입니다.

: 좋은입니다. (해야 합니다 해야할교훈측 : (지야해확은정정합▁lessons)니(▁like다다)와 같은 방법입니다. 문자열 방법은 다음과 같습니다.splitlines됩니다. 수준으로 매우 낮은 수준에서 프로그래밍함으로써 문자열을 함께 배치합니다.+=매우 작은 조각)은 상당히 느릴 수 있습니다.

편집: @Jacob의 제안을 추가하고, 다른 제안과 동일한 결과를 제공하도록 약간 수정했습니다(한 줄의 추적 공백은 유지됨). 즉, 다음과 같습니다.

from cStringIO import StringIO

def f4(foo=foo):
    stri = StringIO(foo)
    while True:
        nl = stri.readline()
        if nl != '':
            yield nl.strip('\n')
        else:
            raise StopIteration

측정 결과:

$ python -mtimeit -s'import asp' 'list(asp.f4())'
1000 loops, best of 3: 406 usec per loop

그다지 좋지 않은.find 접근법 . 왜냐하면 은 작은 원 버그의 기접법근 --그도래반, 기가있다니처럼 +에 덜 할 수 입니다. 왜냐하면 그것은 작은 오프 바이 원 버그(내 것처럼 +1과 -1의 발생을 보는 루프)에 덜 취약할 수 있기 때문입니다.f3위에서 는 자동으로 하나씩 의심을 유발해야 합니다. -- 그리고 그러한 수정이 없고 그것을 가져야 하는 많은 루프도 마찬가지입니다. -- 하지만 저는 다른 함수로 출력을 확인할 수 있었기 때문에 제 코드도 옳다고 믿습니다.')

하지만 분할 기반 접근법은 여전히 지배적입니다.

한 가지 단점: 아마도 더 나은 스타일.f4다음과 같습니다.

from cStringIO import StringIO

def f4(foo=foo):
    stri = StringIO(foo)
    while True:
        nl = stri.readline()
        if nl == '': break
        yield nl.strip('\n')

적어도, 그것은 조금 덜 장황합니다.후행을 .\n안타깝게도 더 명확하고 빠른 교체를 금지합니다.while.return iter(stri) (그)iter현대 버전의 Python에서는 2.3 또는 2.4 이후로 중복되는 부분이 있지만 무해하기도 합니다.)시도해 볼 가치가 있을 수도 마찬가지입니다.

    return itertools.imap(lambda s: s.strip('\n'), stri)

그 -- 저는 에 여기서 the 나그거변이연의론때인습기적여그것문멈다지이렇에기출이서것이는형하또그는것의지만▁or그?지?strip기반, 단순하고 빠른, 하나.

무슨 말인지 잘 모르겠어요 "그럼 다시 파서에게"분할이 완료된 후에는 문자열에 대한 추가적인 트래버설은 없으며 분할 문자열 목록에 대한 트래버설만 수행됩니다.문자열의 크기가 절대적으로 크지 않은 한, 이것이 실제로 이것을 달성하는 가장 빠른 방법이 될 것입니다.python이 불변의 문자열을 사용한다는 것은 항상 새로운 문자열을 만들어야 한다는 것을 의미하기 때문에 어쨌든 이 작업은 어느 시점에서 수행되어야 합니다.

문자열이 매우 크면 메모리 사용량에 단점이 있습니다. 원래 문자열과 분할 문자열 목록이 동시에 메모리에 저장되어 필요한 메모리가 두 배로 증가합니다.반복자 접근 방식을 사용하면 필요에 따라 문자열을 작성하여 이를 절약할 수 있지만, 여전히 "분할" 패널티를 지불합니다.그러나 문자열이 그렇게 클 경우 일반적으로 분할되지 않은 문자열도 메모리에 포함되지 않도록 해야 합니다.파일에서 문자열을 읽기만 하면 됩니다. 파일을 줄로 반복할 수 있습니다.

그러나 메모리에 이미 큰 문자열이 있는 경우 한 가지 접근 방식은 문자열을 사용하는 것입니다.IO: 줄별 반복을 허용하는 것을 포함하여 문자열에 파일과 같은 인터페이스를 제공합니다(내부적으로 .find를 사용하여 다음 줄을 찾습니다).그러면 다음을 얻을 수 있습니다.

import StringIO
s = StringIO.StringIO(myString)
for line in s:
    do_something_with(line)

줄바꿈 문자를 포함하여 줄을 생성하는 "파일"을 반복할 수 있습니다. 파일 "가상 파일"을 사용하면 .StringIO:

import io  # for Py2.7 that would be import cStringIO as io

for line in io.StringIO(foo):
    print(repr(line))

내가 읽는다면,Modules/cStringIO.c정확하게, 이것은 상당히 효율적이어야 합니다(비록 다소 장황하지만).

from cStringIO import StringIO

def iterbuf(buf):
    stri = StringIO(buf)
    while True:
        nl = stri.readline()
        if nl != '':
            yield nl.strip()
        else:
            raise StopIteration

정규식 기반 검색은 때때로 제너레이터 접근 방식보다 빠릅니다.

RRR = re.compile(r'(.*)\n')
def f4(arg):
    return (i.group(1) for i in RRR.finditer(arg))

내 생각엔 네가 직접 굴릴 수 있을 것 같아,

def parse(string):
    retval = ''
    for char in string:
        retval += char if not char == '\n' else ''
        if char == '\n':
            yield retval
            retval = ''
    if retval:
        yield retval

이 구현이 얼마나 효율적인지는 잘 모르겠지만, 문자열 위에 한 번만 반복됩니다.

음, 발전기.

편집:

물론 수행할 구문 분석 작업의 유형에 관계없이 추가할 수도 있지만, 이는 매우 간단합니다.

언급URL : https://stackoverflow.com/questions/3054604/iterate-over-the-lines-of-a-string

반응형