使用正则表达式去除包含数字的双引号

6

我正在处理以下字符串:

'"name": "Gnosis", \n        "symbol": "GNO", \n        "rank": "99", \n        "price_usd": "175.029", \n        "price_btc": "0.0186887", \n        "24h_volume_usd": "753877.0"'

我需要在Python中使用re.sub()函数,仅替换括住数字的双引号("),以便稍后在JSON中解析。我尝试了一些正则表达式,但没有成功。以下是我最好的尝试:

exp = re.compile(r': (")\D+\.*\D*(")', re.MULTILINE)
response = re.sub(exp, "", string)

我已经搜索了很多类似的问题,但没有找到另一个类似的问题。

编辑:

最终我使用了(感谢S. Kablar):

fomatted = re.sub(r'"(-*\d+(?:\.\d+)?)"', r"\1", string)
parsed = json.loads(formatted)

问题在于此端点返回的JSON格式不正确。
其他用户回答说:“首先用json解析字符串,然后使用for循环将数字转换为浮点数”,我认为这是一种非常低效的方法,而且你将被迫选择响应的整数或浮点数类型。为了消除疑虑,我写了这个代码片段,展示了不同方法之间的比较和基准测试,目前我要相信正则表达式在这种情况下的作用。
感谢大家的帮助。

双引号(您称之为逗号)从哪里进入?您是否尝试过像这样的临时解决方法:json.loads('{' + yourstring + '}')并检查一下呢? - Jon Clements
使用正则表达式不是做这种事情的正确方式。如S.Kablar的答案中所述,字符串"\"43"将会被您的正则表达式替换损坏。永远不要在结构化数据上使用正则表达式;请使用预期的解析器。 - Daniel
3个回答

8

正则表达式"(-?\d+(?:[\.,]\d+)?)" 替换字符串\1

详细解释:

  • () 捕获组
  • (?:) 非捕获组
  • \d 匹配数字 (等同于 [0-9])
  • + 重复1次或更多次
  • ? 重复0次或1次
  • \1 第一组。

Python 代码:

def remove_quotes(text):
    return re.sub(r"\"(-?\d+(?:[\.,]\d+)?)\"", r'\1', text)

remove_quotes('"percent_change_7d": "-23.43"') >> "percent_change_7d": -23.43

这对于许多类型的数字和字符串(如“43)不起作用。 - Daniel

2

首先使用json解析字符串,然后将数字转换为浮点数:

string = '{"name": "Gnosis", \n        "symbol": "GNO", \n        "rank": "99", \n        "price_usd": "175.029", \n        "price_btc": "0.0186887", \n        "24h_volume_usd": "753877.0"}'

data = json.loads(string)
response = {}
for key, value in data.items():
    try:
        value = int(value) if value.strip().isdigit() else float(value)
    except ValueError:
        pass
    response[key] = value

这个方法可行,而且比正则表达式更易于维护(即使我仍然对边缘情况持谨慎态度,我也会点赞 :-))。它还可以通过这个答案来适应复杂的JSON值。 - LSerni
这种方法不起作用,因为JSON解析器将数字处理为字符串。更准确地说,该字符串是由此端点返回的响应的一部分:https://api.coinmarketcap.com/v1/ticker - Álvaro Mondéjar
@ÁlvaroMondéjar。你为什么认为它不起作用?你真的试过吗? - ekhumoro
正则表达式和 json.loads() 不更好吗?查看此代码片段 - Álvaro Mondéjar
那又怎样?在Python中,您可以将非浮点数解码为整数。 您可以在 json.JSONDecoder() 文档 中看到。 - Álvaro Mondéjar
显示剩余3条评论

1
你已经接近了答案。你想要保存数字和冒号,因此需要将它们放在括号里,而不是其他的内容中。另外,数字用\d表示,而不是\D(这代表非数字)。
exp = re.compile(r'(: *)"(\d+\.?\d*)"', re.MULTILINE)
response = re.sub(exp, "\\1\\2", string)

\d+\.?\d*  means "a number (or more), a point (or not), any numbers"

边界情况

上面的内容并未涵盖".125",它是没有数字,一个小数点。

如果你将其更改为"\d*.?\d*",那么它将匹配".",因为它是**任意数字",一个小数点,任意数字"。

我认为唯一可行的方法是

 (\d+\.?\d*|\.\d+)

使用 | 表示 "或":所以,要么是数字后面跟着一个点和任意数字(这匹配 "17."),要么是一个点后面跟着至少一个数字。不幸的是,"\d+.?\d+" 无法匹配 "5"。

或者您可以指定这三种情况:

 (\d+|\d+\.?\d+|\.\d+)

首先是整数 (\d+),然后是带或不带小数的浮点数,最后是仅含小数部分且没有前导零。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接