2016-08-03 3 views
4

이 요구 사항에 대해 '고급'파서를 코딩하는 데 문제가 있습니다. (C 아침 식사의 조각처럼 보이지 않는 것). 입력은 문자열이며 ','로 분리되고 '='가 합쳐진 키 값 쌍입니다. 따옴표가있는 문자열에서 키 값 쌍을 추출하는 중

key1=value1,key2=value2 

이 부분은 나에게 값 (") 인용 할 수있다 속여, 그리고 따옴표 안에 ','종료 키를하지 않습니다.

key1=value1,key2="value2,still_value2" 

이 마지막 부분에 나를 위해 그것을 교묘했다 :(루프 범위에서 나는에 대한 의지, 분할 또는 re.split를 사용합니다.

사람이

따옴표 값 만 발생하고 있다고 가정하는 OK인가?이 할 수있는 깨끗한 방법을 설명 할 수있다 백인이 아니야. 페이스 또는 영숫자가 아닌 문자.

+0

예상 출력을 게시 할 수 있습니까? –

+0

두 번째 예제의'key2' 값에 따옴표가 포함되어 있습니까? 즉, 당신의 예제에서'key2'는''value2, still_value2'' 또는''\ "value2, still_value2 \" "'에 매핑됩니까? – EvilTak

답변

3
나는이 작업을 위해 정규 표현식을 사용하여 무리를 줄 것이다

, 구문 분석하려는 언어가 규칙적이 아니기 때문입니다.

여러 키 값 쌍의 문자열이 있습니다. 이것을 파싱하는 가장 좋은 방법은 패턴을 일치시키는 것이 아니라 올바르게 토큰 화하는 것입니다.

shlex이라는 Python 표준 라이브러리에는 POSIX 셸에서 수행되는 구문 분석을 모방하고 필요에 맞게 쉽게 사용자 지정할 수있는 렉서 구현을 제공하는 모듈이 있습니다.

from shlex import shlex 

def parse_kv_pairs(text, item_sep=",", value_sep="="): 
    """Parse key-value pairs from a shell-like text.""" 
    # initialize a lexer, in POSIX mode (to properly handle escaping) 
    lexer = shlex(text, posix=True) 
    # set ',' as whitespace for the lexer 
    # (the lexer will use this character to separate words) 
    lexer.whitespace = item_sep 
    # include '=' as a word character 
    # (this is done so that the lexer returns a list of key-value pairs) 
    # (if your option key or value contains any unquoted special character, you will need to add it here) 
    lexer.wordchars += value_sep 
    # then we separate option keys and values to build the resulting dictionary 
    # (maxsplit is required to make sure that '=' in value will not be a problem) 
    return dict(word.split(value_sep, maxsplit=1) for word in lexer) 

예는 실행

parse_kv_pairs(
    'key1=value1,key2=\'value2,still_value2,not_key1="not_value1"\'' 
) 

출력 : 내가 추가하는 것을 잊었다 그 난 보통 정기적으로 사용하는 대신 shlex을 고수하는 이유 :

{'key1': 'value1', 'key2': 'value2,still_value2,not_key1="not_value1"'} 

편집 표현식 (이 경우 더 빠름)은 gi 나중에 더 많은 입력을 허용해야하는 경우 특히 놀라움이 적습니다. 그런 키 - 값 쌍을 정규 표현식으로 올바르게 구문 분석하는 방법을 찾지 못했습니다. 엔진을 속일 입력 (예 : A="B=\"1,2,3\"")이 항상 있습니다.

이러한 입력에 신경 쓰지 않는다면 (또는 다른 말로하면, 입력이 정규 언어의 정의를 따르도록 할 수 있다면) 정규 표현식은 완벽합니다.

EDIT2 :split는 훨씬 더 분할/슬라이스/가입보다 사용하기 청소기하는 maxsplit 인수가 있습니다. 그의 소리 입력을위한 @cdlane에 감사드립니다!

+1

나는'shlex'가 견고한 생산 솔루션이라고 믿습니다. 그리고 이것은 바로 문제를 해결할 수있는 좋은 예입니다. 그러나이 대답은 나에게있어 모든 우아함을 잃어 버린다. return 문은 동일한 데이터를 두 번 split 한 후 과도한'split()'을 처리하기 위해'join()'을 사용한다. 사전 이해력? 'return dict (word.split (value_sep, maxsplit = 1)을 렉서에서 사용하는 단어는 어떨까요?) ' – cdlane

+0

네, 글을 쓸 때'maxsplit '인수를 잊어 버렸습니다. 추가 할 때 실제로 덜 우아 해졌습니다. 값에'='를 지원합니다. 귀하의 조언을 주셔서 감사합니다, 나는 대답을 편집. – pistache

2

내가 그것을 C 아침 식사의 조각처럼 보이지 않는 모르겠어요하고 기품 :)

data = {} 
original = 'key1=value1,key2="value2,still_value2"' 
converted = '' 

is_open = False 
for c in original: 
    if c == ',' and not is_open: 
     c = '\n' 
    elif c in ('"',"'"): 
     is_open = not is_open 
    converted += c 

for item in converted.split('\n'): 
    k, v = item.split('=') 
    data[k] = v 
5

Split a string, respect and preserve quotes 일부 정규식 마법을 사용하고, 우리가 할 수

import re 

string = 'key1=value1,key2="value2,still_value2"' 

key_value_pairs = re.findall(r'(?:[^\s,"]|"(?:\\.|[^"])*")+', string) 

for key_value_pair in key_value_pairs: 
    key, value = key_value_pair.split("=") 

BioGeek마다 제 생각에 제 정규식을 해석하는 데 Janne Karila이 사용했습니다.이 패턴은 문자열을 쉼표로 분리하지만 프로세스에서 쉼표로 구분 된 섹션을 존중합니다. 여기에는 두 가지 옵션이 있습니다 : 따옴표를 사용하지 않는 문자 실행; 그것이 아니라면 이중 인용 부호가 (백 슬래시) 실행을 완료 문자를 두 번 인용 실행 탈출 :

(?:    # parenthesis for alternation (|), not memory 
[^\s,"]   # any 1 character except white space, comma or quote 
|    # or 
"(?:\\.|[^"])*" # a quoted string containing 0 or more characters 
       # other than quotes (unless escaped) 
)+    # one or more of the above 
+0

정규식 작동 방식에 대한 설명을 추가 할 수 있습니까? – BioGeek

+1

@BioGeek, 귀하의 요청에 따라 시도해 보았습니다. 성공했는지 여부를 알려주세요. – cdlane

+0

cdlane, 설명 주셔서 감사합니다! – BioGeek

3

내가이 정규 표현식 솔루션을 내놓았다 :

import re 
match = re.findall(r'([^=]+)=(("[^"]+")|([^,]+)),?', 'key1=value1,key2=value2,key3="value3,stillvalue3",key4=value4') 

그리고이 "일치"한다 :

for m in match: 
    key = m[0] 
    value = m[1] 
:

[('key1', 'value1', '', 'value1'), ('key2', 'value2', '', 'value2'), ('key3', '"value3,stillvalue3"', '"value3,stillvalue3"', ''), ('key4', 'value4', '', 'value4')] 

그럼 당신은 키와 값을 얻기 위해 루프를 만들 수 있습니다 몇 가지 다른 답변을 바탕으로

1

, 나는 다음과 같은 해결책을했다 :

import re 
import itertools 

data = 'key1=value1,key2="value2,still_value2"' 

# Based on Alan Moore's answer on http://stackoverflow.com/questions/2785755/how-to-split-but-ignore-separators-in-quoted-strings-in-python 
def split_on_non_quoted_equals(string): 
    return re.split('''=(?=(?:[^'"]|'[^']*'|"[^"]*")*$)''', string) 
def split_on_non_quoted_comma(string): 
    return re.split(''',(?=(?:[^'"]|'[^']*'|"[^"]*")*$)''', string) 

split1 = split_on_non_quoted_equals(data) 
split2 = map(lambda x: split_on_non_quoted_comma(x), split1) 

# 'Unpack' the sublists in to a single list. Based on Alex Martelli's answer on http://stackoverflow.com/questions/952914/making-a-flat-list-out-of-list-of-lists-in-python 
flattened = [item for sublist in split2 for item in sublist] 

# Convert alternating elements of a list into keys and values of a dictionary. Based on Sven Marnach's answer on http://stackoverflow.com/questions/6900955/python-convert-list-to-dictionary 
d = dict(itertools.izip_longest(*[iter(flattened)] * 2, fillvalue="")) 

d입니다 결과 다음과 같은 사전 :

{'key1': 'value1', 'key2': '"value2,still_value2"'} 
관련 문제