python 자르기 - 공백으로 문자열 분할-인용 된 부분 문자열 보존-파이썬



뒤에서 리스트 (14)

당신은 shlex 모듈로부터 분할을 원합니다.

>>> import shlex
>>> shlex.split('this is "a test"')
['this', 'is', 'a test']

이것은 당신이 원하는 것을 정확히 수행해야합니다.

나는 다음과 같은 문자열을 가지고있다 :

this is "a test"

나는 따옴표 안에 공백을 무시하면서 공간으로 분할하기 위해 파이썬에 무언가를 쓰려고합니다. 내가 찾고있는 결과는 다음과 같습니다.

['this','is','a test']

추신. 나는 인용문 안에 따옴표가 있으면 어떻게 될지 물어볼 것입니다. 제 신청서에는 결코 일어나지 않을 것입니다.


나는 제안한다.

테스트 문자열 :

s = 'abc "ad" \'fg\' "kk\'rdt\'" zzz"34"zzz "" \'\''

""및 ''도 캡처 할 수 있습니다.

import re
re.findall(r'"[^"]*"|\'[^\']*\'|[^"\'\s]+',s)

결과:

['abc', '"ad"', "'fg'", '"kk\'rdt\'"', 'zzz', '"34"', 'zzz', '""', "''"]

빈 ""및 " '을 무시합니다.

import re
re.findall(r'"[^"]+"|\'[^\']+\'|[^"\'\s]+',s)

결과:

['abc', '"ad"', "'fg'", '"kk\'rdt\'"', 'zzz', '"34"', 'zzz']

이 시도:

  def adamsplit(s):
    result = []
    inquotes = False
    for substring in s.split('"'):
      if not inquotes:
        result.extend(substring.split())
      else:
        result.append(substring)
      inquotes = not inquotes
    return result

일부 테스트 문자열 :

'This is "a test"' -> ['This', 'is', 'a test']
'"This is \'a test\'"' -> ["This is 'a test'"]

shlex.split을 사용하여 70,000,000 라인의 오징어 로그를 처리합니다. 너무 느립니다. 그래서 나는 다시 바꿨다.

shlex에 성능 문제가있는 경우이 방법을 사용해보십시오.

import re

def line_split(line):
    return re.findall(r'[^"\s]\S*|".+?"', line)

간단한 문자열보다 하위 문자열에 관심이 없다면

>>> 'a short sized string with spaces '.split()

공연:

>>> s = " ('a short sized string with spaces '*100).split() "
>>> t = timeit.Timer(stmt=s)
>>> print "%.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000)
171.39 usec/pass

또는 문자열 모듈

>>> from string import split as stringsplit; 
>>> stringsplit('a short sized string with spaces '*100)

성능 : 문자열 모듈이 문자열 메서드보다 성능이 좋은 것 같습니다.

>>> s = "stringsplit('a short sized string with spaces '*100)"
>>> t = timeit.Timer(s, "from string import split as stringsplit")
>>> print "%.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000)
154.88 usec/pass

또는 RE 엔진을 사용할 수 있습니다.

>>> from re import split as resplit
>>> regex = '\s+'
>>> medstring = 'a short sized string with spaces '*100
>>> resplit(regex, medstring)

공연

>>> s = "resplit(regex, medstring)"
>>> t = timeit.Timer(s, "from re import split as resplit; regex='\s+'; medstring='a short sized string with spaces '*100")
>>> print "%.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000)
540.21 usec/pass

매우 긴 문자열의 경우 전체 문자열을 메모리에로드하지 말고 대신 줄을 분할하거나 반복 루프를 사용해야합니다.


흠, "Reply"버튼을 찾지 못하는 것 같습니다 ... 어쨌든,이 답변은 Kate의 접근법을 기반으로하지만, 이스케이프 된 따옴표가 포함 된 부분 문자열로 문자열을 올바르게 분리하고 부분 문자열의 시작 및 끝 따옴표도 제거합니다.

  [i.strip('"').strip("'") for i in re.split(r'(\s+|(?<!\\)".*?(?<!\\)"|(?<!\\)\'.*?(?<!\\)\')', string) if i.strip()]

이것은 'This is " a \\\"test\\\"\\\'s substring"' 과 같은 문자열에서 작동합니다 (불행히도 Python이 이스케이프를 제거하지 못하게하려면 미안한 마크 업이 필요합니다).

반환 된 목록의 결과 이스케이프가 필요없는 경우이 약간 변경된 함수 버전을 사용할 수 있습니다.

[i.strip('"').strip("'").decode('string_escape') for i in re.split(r'(\s+|(?<!\\)".*?(?<!\\)"|(?<!\\)\'.*?(?<!\\)\')', string) if i.strip()]

따옴표를 유지하려면이 함수를 사용하십시오.

def getArgs(s):
    args = []
    cur = ''
    inQuotes = 0
    for char in s.strip():
        if char == ' ' and not inQuotes:
            args.append(cur)
            cur = ''
        elif char == '"' and not inQuotes:
            inQuotes = 1
            cur += char
        elif char == '"' and inQuotes:
            inQuotes = 0
            cur += char
        else:
            cur += char
    args.append(cur)
    return args

shlex 모듈, 특히 shlex.split .

>>> import shlex
>>> shlex.split('This is "a test"')
['This', 'is', 'a test']

일부 Python 2 버전에서 유니 코드 문제를 해결하기 위해 다음과 같이 제안합니다.

from shlex import split as _split
split = lambda a: [b.decode('utf-8') for b in _split(a.encode('utf-8'))]

복잡한 정규 표현식이나 잘못된 표현을 볼 수 있습니다. 이것은 정규식 구문이 "공백 또는 인용 부호로 둘러싸인"것을 쉽게 기술 할 수 있고, 대부분의 정규식 엔진 (파이썬 포함)이 정규식으로 분리 될 수 있기 때문에 저를 놀라게합니다. 따라서 정규 표현식을 사용하려는 경우 정확히 무슨 뜻인지 말하지 않으시겠습니까? :

test = 'this is "a test"'  # or "this is 'a test'"
# pieces = [p for p in re.split("( |[\\\"'].*[\\\"'])", test) if p.strip()]
# From comments, use this:
pieces = [p for p in re.split("( |\\\".*?\\\"|'.*?')", test) if p.strip()]

설명:

[\\\"'] = double-quote or single-quote
.* = anything
( |X) = space or X
.strip() = remove space and empty-string separators

하지만 shlex는 더 많은 기능을 제공 할 것입니다.


유스 케이스에 따라 csv 모듈을 체크 아웃 할 수도 있습니다.

import csv
lines = ['this is "a string"', 'and more "stuff"']
for row in csv.reader(lines, delimiter=" "):
    print row

산출:

['this', 'is', 'a string']
['and', 'more', 'stuff']

이 질문에 정규식 태그가 지정 되었기 때문에 정규식 접근 방식을 사용하기로 결정했습니다. 먼저 quotes 부분의 모든 공백을 \ x00으로 바꾸고 공백으로 나눈 다음 \ x00을 각 부분의 공백으로 다시 바꿉니다.

두 버전 모두 똑같은 일을하지만, 스플리터는 약간 더 읽기 쉽고 스플리터 2입니다.

import re

s = 'this is "a test" some text "another test"'

def splitter(s):
    def replacer(m):
        return m.group(0).replace(" ", "\x00")
    parts = re.sub('".+?"', replacer, s).split()
    parts = [p.replace("\x00", " ") for p in parts]
    return parts

def splitter2(s):
    return [p.replace("\x00", " ") for p in re.sub('".+?"', lambda m: m.group(0).replace(" ", "\x00"), s).split()]

print splitter2(s)

다른 답변의 속도 테스트 :

import re
import shlex
import csv

line = 'this is "a test"'

%timeit [p for p in re.split("( |\\\".*?\\\"|'.*?')", line) if p.strip()]
100000 loops, best of 3: 5.17 µs per loop

%timeit re.findall(r'[^"\s]\S*|".+?"', line)
100000 loops, best of 3: 2.88 µs per loop

%timeit list(csv.reader([line], delimiter=" "))
The slowest run took 9.62 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 2.4 µs per loop

%timeit shlex.split(line)
10000 loops, best of 3: 50.2 µs per loop

(오래된) 파이썬 문서에서 직접 (itertools의 요리법) :

from itertools import izip, chain, repeat

def grouper(n, iterable, padvalue=None):
    "grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
    return izip(*[chain(iterable, repeat(padvalue, n-1))]*n)

JFSebastian에서 제안한대로 현재 버전 :

#from itertools import izip_longest as zip_longest # for Python 2.x
from itertools import zip_longest # for Python 3.x
#from six.moves import zip_longest # for both (uses the six compat library)

def grouper(n, iterable, padvalue=None):
    "grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
    return zip_longest(*[iter(iterable)]*n, fillvalue=padvalue)

귀도의 타임머신은 작동합니다. 작동 할 것입니다. 다시 작동 할 것입니다.

이러한 솔루션은 [iter(iterable)]*n (또는 이전 버전과 등가물)이 목록에서 n 번 반복 된 하나의 반복자를 생성하기 때문에 작동합니다. izip_longest 는 "각"반복자의 라운드 로빈을 효과적으로 수행합니다. 이것은 동일한 반복자이기 때문에 각 호출에 의해 앞당겨지며 각 zip-roundrobin은 n 항목으로 구성된 하나의 튜플을 생성합니다.





python regex