如何仅在“顶层”处分割字符串,考虑引号和括号?

-1 投票
2 回答
112 浏览
提问于 2025-04-13 15:41

我想写一个函数,这个函数可以接收一个用逗号分隔的字符串,然后把它分开,类似于 str.split(),但要保留那些可能是嵌套的引号和括号部分。

比如说,这个字符串可能是用逗号分隔的键值对(像 a=b,c=d),也可能是用逗号分隔的命令,这些命令里面可能会包含分隔符,甚至是引号字符。你可以用这些来写一个简单的正则表达式。

为了避免写一个完整的解析器,我最开始的想法是使用 csv 模块(可以参考 如何处理csv模块中字段值内的双引号?),但即使在简单的情况下,我也没能成功使用它,例如:

>>> s = "a='[1,2,3]',c=d"
>>> list(csv.reader([s], delimiter=',', quotechar="'"))  # expected: ["a='[1,2,3]'", "c=d"]
[["a='[1", '2', "3]'", 'c=d']]

而且我还没有尝试更复杂的情况,比如 a='[1,"3,14",3]',c="[4,5,6]"

有没有简单的方法可以让 csv.reader 把上面的字符串分割成 ['a=..', 'c=..'],或者更好的是,使用一些内置的字符串处理功能呢?

2 个回答

0

这里有一个对我有效的函数,它可以接受自定义的分隔符:

def smart_split(string: str, delimiter: str = ",") -> Iterable[str]:
    """Like str.split but takes quotes and parenthesis into account
    >>> list(smart_split("(=,),'a=x',\\"b=y\\""))
    ['(=,)', "'a=x'", '"b=y"']
    """
    splits: list[int] = []
    closers: list[str] = []
    parenthizers = {'"': '"', "'": "'", "[": "]", "(": ")"}
    for i, char in enumerate(string):
        if char == delimiter and not closers:
            splits.append(i)
        elif closers and char == closers[-1]:
            closers.pop()
        elif char in parenthizers:
            closers.append(parenthizers[char])
    start = 0
    for pos in splits:
        yield string[start:pos]
        start = pos + 1
    yield string[start:]

不过要注意,这个函数没有进行一致性检查,也不支持转义的括号或其他任何东西。

1

好吧,对于你的特定数据,你可以稍微“作弊”一下,因为如果你仔细看,会发现它看起来像是Python调用时的关键字参数(kwargs)。所以你可以把它放在一个调用表达式里,然后解析抽象语法树(AST)。

import ast

target = """
a='[1,"3,14",3]',c="[4,5,6]"
"""

tree = ast.parse(f"XXX({target.strip()})", "...", "eval")

print({kw.arg: ast.unparse(kw.value) for kw in tree.body.keywords})

输出结果是

{'a': '\'[1,"3,14",3]\'', 'c': "'[4,5,6]'"}

撰写回答