Мне нужно разобрать входную строку в python и извлечь из нее определенные части.
формат строки
(xx,yyy,(aa,bb,...)) // Inner parenthesis can hold one or more characters in it
Я хочу, чтобы функция возвращала xx, yyyy и список, содержащий aa, bb... etc
Я могу, конечно, сделать это, пытаясь разделить скобки и прочее, но я хочу знать, есть ли правильный питонический способ извлечения такой информации из строки
У меня есть этот код, который работает, но есть ли лучший способ сделать это (без регулярного выражения)
def processInput(inputStr):
value = inputStr.strip()[1:-1]
parts = value.split(',', 2)
return parts[0], parts[1], (parts[2].strip()[1:-1]).split(',')
Если у вас аллергия на RE, вы можете использовать pyparsing:
>>> import pyparsing as p
>>> ope, clo, com = map(p.Suppress, '(),')
>>> w = p.Word(p.alphas)
>>> s = ope + w + com + w + com + ope + p.delimitedList(w) + clo + clo
>>> x = '(xx,yyy,(aa,bb,cc))'
>>> list(s.parseString(x))
['xx', 'yyy', 'aa', 'bb', 'cc']
pyparsing
также позволяет легко контролировать точную форму результатов (например, группируя последние 3 элемента в свой собственный подсписок), если хотите. Но я думаю, что самый приятный аспект - как естественный (в зависимости от того, сколько места вы хотите посвятить ему), вы можете сделать "грамматическую спецификацию" прочитанной: открытый парен, слово, запятую, слово, запятую, открытую paren, разделительный список слов, две закрытые круглые скобки (если вы считаете, что присвоение s
выше не так просто читать, я полагаю, что это моя ошибка за то, что вы не выбрали более длинные идентификаторы; -).
Если вложенность ваших скобок может быть сколь угодно глубокой, то regexen не будет работать, вам понадобится конечный автомат или синтаксический анализатор. Pyparsing поддерживает рекурсивные грамматики с использованием класса forward-declaration Forward:
from pyparsing import *
LPAR,RPAR,COMMA = map(Suppress,"(),")
nestedParens = Forward()
listword = Word(alphas) | '...'
nestedParens << Group(LPAR + delimitedList(listword | nestedParens) + RPAR)
text = "(xx,yyy,(aa,bb,...))"
results = nestedParens.parseString(text).asList()
print results
text = "(xx,yyy,(aa,bb,(dd,ee),ff,...))"
results = nestedParens.parseString(text).asList()
print results
Печать
[['xx', 'yyy', ['aa', 'bb', '...']]]
[['xx', 'yyy', ['aa', 'bb', ['dd', 'ee'], 'ff', '...']]]
pyparsing
то время как я придерживался самых основ ;-)
Как насчет этого?
>>> import ast
>>> import re
>>>
>>> s="(xx,yyy,(aa,bb,ccc))"
>>> x=re.sub("(\w+)",'"\\1"',s)
# '("xx","yyy",("aa","bb","ccc"))'
>>> ast.literal_eval(x)
('xx', 'yyy', ('aa', 'bb', 'ccc'))
>>>
Пусть используются регулярные выражения!
/\(([^,]+),([^,]+),\(([^)]+)\)\)/
Соответствует этому, первая группа захвата содержит xx, вторая содержит yyy, разделяет третью на ,
, и у вас есть свой список.
Я не знаю, что это лучше, но это другой способ сделать это. Использование ранее предложенного регулярного выражения
def processInput(inputStr):
value = [re.sub('\(*\)*','',i) for i in inputStr.split(',')]
return value[0], value[1], value[2:]
Кроме того, вместо регулярного выражения вы можете использовать две связанные функции замены.
Ваше решение достойное (простое, эффективное). Вы можете использовать регулярные выражения, чтобы ограничить синтаксис, если вы не доверяете источнику данных.
import re
parser_re = re.compile(r'\(([^,)]+),([^,)]+),\(([^)]+)\)')
def parse(input):
m = parser_re.match(input)
if m:
first = m.group(1)
second = m.group(2)
rest = m.group(3).split(",")
return (first, second, rest)
else:
return None
print parse( '(xx,yy,(aa,bb,cc,dd))' )
print parse( 'xx,yy,(aa,bb,cc,dd)' ) # doesn't parse, returns None
# can use this to unpack the various parts.
# first,second,rest = parse(...)
Печать
('xx', 'yy', ['aa', 'bb', 'cc', 'dd'])
None
eval()
, хотя я, конечно, не рекомендовал бы это :)