在打乱的字符串中匹配打开和关闭括号

时间:2015-04-30 18:17:04

标签: python regex

如何将随机字符串(包含各种字符)解析为连贯的内容?

例如,string = '{"letters" : '321"}{}"'}{'{}{{}"': "stack{}}{"}' 我想分开: {"letters" : '321"}{}"'}{'{}{{}"': "stack{}}{"}

我已尝试遍历string并计算每个左括号{,并在显示近距离}时减去。但是,这不起作用,因为有些情况下括号位于""''内 我的代码是:

list1 = []  # list1 is where we build up the first string
list2 = []  # list2 is where we keep the strings after building
for c in string:
    list1.append(c)
    if c == "{":
        bracket_counter += 1
    elif c == "}":
        bracket_counter -= 1
        if bracket_counter == 0:
            list2.append("".join(item)) 
            list1 = []

使用此代码,第一个被认为是"完成"是{"letters" : '321"},即使它应该是{"letters" : '321"}{}"'}

我对正则表达式很不熟悉,所以我不确定这是否应该用于它。任何帮助表示赞赏。

谢谢!

3 个答案:

答案 0 :(得分:2)

您使用正则表达式标记化您的字符串,然后您将迭代这些标记。例如:

SQ = r"'[^']*'"   # single-quoted string
DQ = r'"[^"]*"'   # double-quoted string
OPS = r'[{}:]'    # operators
WS = r'\s+'       # whitespace
     # add more types as needed...
tokens = '(?:' + '|'.join([OPS, SQ, DQ, WS]) + ')'
pattern = re.compile(tokens, re.DOTALL)

def tokenize(source):
    start = 0
    end = len(source)
    while start < end:
        match = pattern.match(source, start)
        if match:
            yield match.group(0)
        else:
            raise ValueError('Invalid syntax at character %d' % start)

        start = match.end()

然后您可以在这些令牌上运行for循环:

for token in tokenize(string):
    ...

示例输入时的标记为:

>>> for token in tokenize(string):
...     print(token)
'{'
'"letters"'
' '
':'
' '
'\'321"}{}"\''
'}'
'{'
'\'{}{{}"\''
':'
' '
'"stack{}}{"'
'}'

正如您所看到的,您可以从中正确计算'{''}'

请注意,上面的正则表达式没有转义字符串中'"的概念;如果您希望\转义结束字母并正确标记,则可以将SQDQ正则表达式更改为

SQ = r"'(?:[^\\']|\\.)*'"
DQ = r'"(?:[^\\"]|\\.)*"'

此外,如果您还希望允许任何其他字符但未特别处理,则可以添加

NON_SPECIAL = r'[^\'"]'

作为正则表达式的最后一个分支:

tokens = '(?:' + '|'.join([OPS, SQ, DQ, WS, NON_SPECIAL]) + ')'

答案 1 :(得分:0)

您还必须检查您是否在字符串中。一个简单的方法是创建另一个变量并跳过循环,如果你在一个字符串中并且它不是结束字符。

bracket_counter = 0
quote = ""
list1 = []  # list1 is where we build up the first string
list2 = []  # list2 is where we keep the strings after building
for c in string:
    list1.append(c)
    if not quote or c == quote:  # If quote is blank or found the closing quote
        quote = ""
        if c == "{":
            bracket_counter += 1
        elif c == "}":
            bracket_counter -= 1
            if bracket_counter == 0:
                list2.append("".join(item)) 
                list1 = []
        elif c in "'\"":  # If the character is a quote character
            quote = c  # Will skip loops until quote is found

如果你想要一个正则表达式,你首先会效仿:

{.*?}

但是你想忽略引号,所以你会这样做:

{((".*?")|('.*?')|.)*?}

基本上,这利用了懒惰的量词。它试图找到引用的东西为&#34; ...&#34;,然后&#39; ...&#39;然后最终挑选任何角色。

如果您不想使用延迟量词,请使用正则表达式:

{("[^"]*"|'[^']*'|[^{}])*}

这给出了代码:

import re

def parse(s):
    return [group[0] for group in re.findall("({((\".*?\")|('.*?')|.)*?})", s)]

用法:

>>> string = """{"letters" : '321"}{}"'}{'{}{{}"': "stack{}}{"}"""
>>> parse(string)
['{"letters" : \'321"}{}"\'}', '{\'{}{{}"\': "stack{}}{"}']
>>> print(", ".join(parse(string)))
{"letters" : '321"}{}"'}, {'{}{{}"': "stack{}}{"}

答案 2 :(得分:0)

这是一个 Python 问题,但您可以移植 javascript 包 dqtokenizer https://www.npmjs.com/package/dqtokenizer 代码以更轻松地执行此操作。

    testTokenize(`{"letters" : '321"}{}"'}{'{}{{}"': "stack{}}{"}`, {
        additionalBoundaryChars: [],
        singleCharTokens: ['(', ')', '{', '}', '[', ']', ':'],
    });

输出:

tokens:
            0: {
            1: "letters"
            2: :
            3: '321"}{}"'
            4: }
            5: {
            6: '{}{{}"'
            7: :
            8: "stack{}}{"
            9: }