【问题标题】:Surround any word with a character if the word match a pattern [duplicate]如果单词与模式匹配,则用字符包围任何单词[重复]
【发布时间】:2014-07-02 09:53:22
【问题描述】:

我有一个这样的输入字符串:

'{ query: { and: [ { and: [ { _t: "Manifest" }, { or: [ { and: [ { _i: { gt: "53b2616fe4b028359ac3fea4" } } ] } ] }, { _s: "active" } ] }, { ENu_v: { elemMatch: { EOJ_v: { in: [ "*", "Production", "QA    " ] } } } } ] }, orderby: { _i: 1 } } '

我想把它改成字典。

a = '{ query: { and: [ { and: [ { _t: "Manifest" }, { or: [ { and: [ { _i: { gt: "53b2616fe4b028359ac3fea4" } } ] } ] }, { _s: "active" } ] }, { ENu_v: { elemMatch: { EOJ_v: { in: [ "*", "Production", "QA    " ] } } } } ] }, orderby: { _i: 1 } } '

json.loads(a)

但这会抛出异常,因为 query 应该是 "query"and 应该是 "and" 等等。

所以我想将string 之类的所有字符串更改为"string",我该如何实现?

【问题讨论】:

  • 它从何而来?如果您或您认识的人创建了它,我会建议在那里修复它。否则,您必须进行自己的自定义解析以区分“标识符”和其他项目({、[、(、:、整数等)
  • 我建议查看stackoverflow.com/questions/8815586/…stackoverflow.com/questions/18280279/… 以获取任何正则表达式示例。也许他们会在你的“json”上工作。
  • 相关:Converting str to dict in python,它还将 JavaScript 输出修复为 JSON,并添加了一个替代库来解析它而无需正则表达式技巧。

标签: python regex


【解决方案1】:

使用re.sub:

In [1]: import re

In [2]: text = '{ query: { and: [ { and: [ { _t: "Manifest" }, { or: [ { and: [ { _i: { gt: "53b2616fe4b028359ac3fea4" } } ] } ] }, { _s: "active" } ] }, { ENu_v: { elemMatch: { EOJ_v: { in: [ "*", "Production", "QA    " ] } } } } ] }, orderby: { _i: 1 } } '

In [3]: re.sub('(\w+):', r'"\1":', text)
Out[3]: '{ "query": { "and": [ { "and": [ { "_t": "Manifest" }, { "or": [ { "and": [ { "_i": { "gt": "53b2616fe4b028359ac3fea4" } } ] } ] }, { "_s": "active" } ] }, { "ENu_v": { "elemMatch": { "EOJ_v": { "in": [ "*", "Production", "QA    " ] } } } } ] }, "orderby": { "_i": 1 } } '

请注意,您必须使用原始字符串文字(或将 \1 转义为 \\1)作为替换文本,否则您将无法获得预期的输出。


我假设您的文字不包含“奇怪”的内容,例如:

  • 值中的冒号(例如{a: "some:string"};此解决方案不保留"some:string"
  • 包含嵌套结构的复杂字符串(例如{a: "{b : \"hello\"}"}

如果这些假设不成立,您必须实际解析文本,并且您不能仅使用正则表达式安全地对其进行转换。

ast 模块与codegen 第三方模块一起使操作此类数据变得容易。例如,您可以创建一个NodeTransformer 子类,例如:

class QuoteNames(ast.NodeTransformer):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._inside_dict = False
    def visit_Name(self, node):
        if self._inside_dict:
            return ast.copy_location(ast.Str(node.id), node)
        else:
            return node
    def visit_Dict(self, node):
        self._inside_dict = True
        self.generic_visit(node)
        self._inside_dict = False
        return node

并将其用作:

import ast, codegen
codegen.to_source(QuoteNames().visit(ast.parse(text))

但是,您的示例文本不是语法有效的文字,因为某些括号不匹配(这可能是您的示例中的错误),有一些字符串值缺少结束引号,您不能使用 andor 在标识符中。

如果您可以修复格式以匹配 python 语法,那么上述解决方案比使用正则表达式的解决方案更强大。但是,如果这不可能,您必须为它编写自己的解析器,或者寻找能够做到这一点的第三方模块。

【讨论】:

  • 这将在任何已经包含“:”的单词上失败,例如输入 '{ query: "bla:a"}'。
  • 我以前用过re.sub(r'(?:^|(?<=[{,]))\s*(\w+)(?=:)', r' "\1"', text, flags=re.M),寻找逗号或左大括号或行首。
  • @SébastienDeprez 是的,但是在 OP 示例中没有这样的事情,所以我假设 text 很简单。显然,如果您想考虑所有内容(包括值字符串中的嵌套“dicts”之类的内容),您必须实际解析该内容,因为正则表达式的作用与正则表达式无法解析 HTML 的方式相同。
【解决方案2】:

您可以匹配以下内容:

'(\w+):'

并替换为:

'"\1":'

其中\1 是第一个捕获的组。

你可以在这里看到它的实际效果:DEMO

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-27
    • 1970-01-01
    • 2018-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-11
    相关资源
    最近更新 更多