5

我正在寻找一种方法来扩展由斜杠分隔的数字。除了斜线,括号可以用在一些(或所有)数字周围来表示一个“组”,它可以重复(直接在括号后面的次数)或反向重复(后面跟着's'作为在第二组示例中显示)。一些例子是:

1          -> ['1']                          -> No slashes, no parentheses
1/2/3/4    -> ['1', '2', '3', '4']           -> basic example with slashes
1/(2)4/3   -> ['1', '2', '2', '2', '2', '3'] -> 2 gets repeated 4 times
1/(2/3)2/4 -> ['1', '2', '3', '2', '3', '4'] -> 2/3 is repeated 2 times
(1/2/3)2   -> ['1', '2', '3', '1', '2', '3'] -> Entire sequence is repeated twice
(1/2/3)s   -> ['1', '2', '3', '3', '2', '1'] -> Entire sequence is repeated in reverse
1/(2/3)s/4 -> ['1', '2', '3', '3', '2', '4'] -> 2/3 is repeated in reverse

在最一般的情况下,甚至可能有嵌套的括号,我知道这通常会使正则表达式的使用变得不可能。在我需要处理的当前数据集中,没有这样的嵌套集,但我可以看到它在未来的潜在用途。例如:

1/(2/(3)2/4)s/5 -> 1/(2/3/3/4)s/5
                -> 1/2/3/3/4/4/3/3/2/5
                -> ['1', '2', '3', '3', '4', '4', '3', '3', '2', '5']

我当然知道正则表达式不能完成所有这些(尤其是重复/反转括号集)。但是,如果我能得到一个正则表达式,它至少将括号中的字符串与括号中的字符串分开,那么我可能可以很容易地制作一些循环来处理其余部分。所以,我正在寻找的正则表达式会做这样的事情:

1               -> ['1']
1/2/3/4         -> ['1', '2', '3', '4']
1/(2)4/3        -> ['1', '(2)4', '3']
1/(2/3)2/4      -> ['1', '(2/3)2', '4']
1/(2/(3)2/4)s/5 -> ['1', '(2/(3)/2/4)s', '5']

然后我可以循环这个结果并继续扩展任何括号,直到我只有数字。

编辑

我在原始帖子中并不完全清楚。在我试图使示例尽可能简单的过程中,我可能过度简化了它们。这需要适用于 >= 10 的数字以及负数。

例如:

1/(15/-23)s/4   -> ['1', '(15/-23)s', '4']
                -> ['1', '15', '-23', '-23', '15', '4']
4

3 回答 3

3

这是完成此任务的另一种方法。

def expand(string):

    level = 0
    buffer = ""
    container = []

    for char in string:

        if char == "/":
            if level == 0:
                container.append(buffer)
                buffer = ""
            else:
                buffer += char
        elif char == "(":
            level += 1
            buffer += char
        elif char == ")":
            level -= 1
            buffer += char
        else:
            buffer += char

    if buffer != "":
        container.append(buffer)

    return container
于 2013-08-14T22:40:31.050 回答
3

由于您正在处理嵌套括号,因此正则表达式在这里帮不上什么忙。它不能像您最后想要的那样轻松地将字符串转换为列表。

您最好自己解析字符串。您可以尝试此代码,只是为了最终满足您的要求:

在不丢失括号的情况下将字符串解析为列表:

def parse(s):

    li = []

    open = 0
    closed = False
    start_index = -1

    for index, c in enumerate(s):
        if c == '(':
            if open == 0:
                start_index = index
            open += 1

        elif c == ')':
            open -= 1
            if open == 0:
                closed = True

        elif closed:
            li.append(s[start_index: index + 1])
            closed = False

        elif open == 0 and c.isdigit():
            li.append(c)

    return li

这将为字符串'1/(2/(3)2/4)s/5'提供以下列表:

['1', '(2/(3)2/4)s', '5']

对于 string '1/(15/-23)s/4',根据您更改的要求,这给出:

['1', '(15/-23)s', '4']

现在,您需要注意进一步打破括号以获得不同的列表元素。


将带括号的字符串扩展为单个列表元素:

在这里,您可以通过一次处理最内层的括号来使用正则表达式:

import re

def expand(s):

    ''' Group 1 contains the string inside the parenthesis
        Group 2 contains the digit or character `s` after the closing parenthesis

    '''    
    match = re.search(r'\(([^()]*)\)(\d|s)', s)
    if match:
        group0 = match.group()
        group1 = match.group(1)
        group2 = match.group(2)
        if group2.isdigit():
            # A digit after the closing parenthesis. Repeat the string inside
            s = s.replace(group0, ((group1 + '/') * int(group2))[:-1])
        else:
            s = s.replace(group0, '/'.join(group1.split('/') + group1.split('/')[::-1]))

    if '(' in s:
        return expand(s)

    return s

li = parse('1/(15/-23)2/4')

for index, s in enumerate(li):
    if '(' in s:
        s = expand(s)
        li[index] = s.split('/')

import itertools
print list(itertools.chain(*li))

这将为您提供所需的结果:

['1', '15', '-23', '-23', '15', '4']

上面的代码遍历parse(s)方法生成的列表,然后对于每个元素,递归地扩展最内层的括号。

于 2013-08-14T22:34:44.300 回答
0

正则表达式对于这项工作来说是完全错误的工具。关于为什么正则表达式不合适有一个冗长的解释(如果你想知道为什么,这里有一个在线课程)。一个简单的递归解析器很容易编写来处理这个问题,您可能在完成正则表达式调试之前就已经完成了它。

这是一个缓慢的一天,所以我自己写了它,并完成了文档测试。

def parse(s):
    """
    >>> parse('1')
    ['1']
    >>> parse('1/2/3/4')
    ['1', '2', '3', '4']
    >>> parse('1/(2)4/3')
    ['1', '2', '2', '2', '2', '3']
    >>> parse('1/(2/3)2/4')
    ['1', '2', '3', '2', '3', '4']
    >>> parse('(1/2/3)2')
    ['1', '2', '3', '1', '2', '3']
    >>> parse('1/(2/3)s/4')
    ['1', '2', '3', '3', '2', '4']
    >>> parse('(1/2/3)s')
    ['1', '2', '3', '3', '2', '1']
    >>> parse('1/(2/(3)2/4)s/5')
    ['1', '2', '3', '3', '4', '4', '3', '3', '2', '5']
    """
    return _parse(list(s))


def _parse(chars):
    output = []
    while len(chars):
        c = chars.pop(0)
        if c == '/':
            continue
        elif c == '(':
            sub = _parse(chars)
            nextC = chars.pop(0)
            if nextC.isdigit():
                n = int(nextC)
                sub = n * sub
                output.extend(sub)
            elif nextC == 's':
                output.extend(sub)
                output.extend(reversed(sub))
        elif c == ')':
            return output
        else:
            output.extend(c)

    return output

if __name__ == "__main__":
    import doctest
    doctest.testmod()
于 2013-08-14T23:10:02.230 回答