如何仅在“顶层”处分割字符串,考虑引号和括号?
我想写一个函数,这个函数可以接收一个用逗号分隔的字符串,然后把它分开,类似于 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]'"}