I have string in the form 'AB(AB(DDC)C)A(BAAC)DAB(ABC)'
.
- Each character represents an element (
A
,B
,C
orD
). - Between parentheses, on the right, there is the child of each element (which may be absent).
In example, having 'AB(AB(DDC)C)A(BAAC)DA'
, the top level would be AB(AB(DDC)C)A(BAAC)DA --> [A, B, A, D, A]
and the corresponding children would be [None, AB(DDC)C, BAAC, None, None]
. The children are to be parsed as well recursively.
I have implemented a solution here:
def parse_string(string):
i = 0
parsed = []
while i < len(string):
if string[i] in ('A', 'B', 'C', 'D'):
parsed.append([string[i], None])
i += 1
elif string[i] == '(':
open_brakets = 1
i += 1
j = i
while open_brakets:
if string[j] == '(':
open_brakets += 1
elif string[j] == ')':
open_brakets -= 1
j += 1
# Parse the children as well
parsed[-1][-1] = parse_string(string[i:j - 1])
i = j
else:
i += 1
return parsed
print parse_string('AB(AB(DDC)C)A(BAAC)DAB(ABC)')
Although I think it's a bit ugly and I'm sure it is not very efficient.
I wonder if there's a way to make this with Python in a cleaner/faster/more elegant way? Using external libraries is allowed (specially if they're written in C! :-P).
Update
Other examples of strings that should work:
ABC(DAB(ACB)BBB(AAA)ABC)DCB
In general, the length of the string is not limited, neither the number of children, nor their length, nor the number of nested levels.
As far as being a bit ugly, that's in the eye of the beholder.
As far as speed is concerned, it will be hard to improve on your code.
ADDED: Here is how I would do it in C++. You can adapt to Python if you care. This shows how to do it with recursion. The top-level function is
topLevel("blah blah")
.You can use
regex
for parsing your text.As a more general string consider the following string :
You can use
re.findall
to find the outer pattern :And use that regex with
re.split
to get the strings bound within parenthesis :A brife explain about the preceding regex :
This regex is contain of 2 part that concatenates with pip token (
|
) that works as logicor
:(?<=\))\w+(?=\(|$)
:this regex will match any combination of word characters (
\w+
) that precede by)
and followed by(
or$
that $ is the end of string modifier that match the end of string.Note using
$
is for the caseDDD
!^\w+(?=\()
:this regex will match any combination of word characters that appears at the start of string (modifier
^
will match the start of string) and followed by(
If you need to recursively parse the inner parentheses as well:
The way this works can be thought of like a state machine. The state machine accepts node definitions until it sees an open parentheses, in which it pushes a new context (i.e. a recursive function call) to the parsing stack to parse the content of the parentheses. When parsing the inner context, the close parentheses pops the context.
Another alternative, which can scale better if you have more complex grammars is to use a parsing library like PyParsing:
Parsing libraries like PyParsing allows you to define an easy-to-read declarative grammar.
Answer to original non-recursive parsing: One way to do this is with itertools (accumulate is only from Python 3.2 and up, the itertools docs has a pure python implementation of accumulate for use in older versions). This avoids the use of indices:
I'm not quite sure whether it's faster or more elegant, but I think using explicit indexes is much easier to write, understand, and modify.