如何限制JSONEncoder产生的浮点数的数量? [英] How to limit the number of float digits JSONEncoder produces?

查看:70
本文介绍了如何限制JSONEncoder产生的浮点数的数量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试设置python json库,以便将包含其他字典作为元素的字典保存到文件中.浮点数很多,我想将位数限制为例如7.

I am trying to set the python json library up in order to save to file a dictionary having as elements other dictionaries. There are many float numbers and I would like to limit the number of digits to, for example, 7.

应使用SO encoder.FLOAT_REPR上的其他帖子.但是它不起作用.

According to other posts on SO encoder.FLOAT_REPR shall be used. However it is not working.

例如下面的代码在Python3.7.1中运行,将打印所有数字:

For example the code below, run in Python3.7.1, prints all the digits:

import json
json.encoder.FLOAT_REPR = lambda o: format(o, '.7f' )
d = dict()
d['val'] = 5.78686876876089075543
d['name'] = 'kjbkjbkj'
f = open('test.json', 'w')
json.dump(d, f, indent=4)
f.close()

我该如何解决?

可能无关紧要,但我在macOS上.

It might be irrelevant but I am on macOS.

编辑

此问题被标记为重复.但是,在原始帖子所接受的答案(到目前为止是唯一的答案)中明确指出:

This question was marked as duplicated. However in the accepted answer (and until now the only one) to the original post it is clearly stated:

注意:此解决方案不适用于python 3.6 +

Note: This solution doesn't work on python 3.6+

因此,该解决方案不是正确的解决方案.另外,它使用的是库simplejson 不是json.

So that solution is not the proper one. Plus it is using the library simplejson not the library json.

推荐答案

选项1:使用正则表达式匹配进行舍入.

您可以使用json.dumps将对象转储为字符串,然后使用本文中显示的技术查找并舍入您的浮点数.

Option 1: Use regular expression matching to round.

You can dump your object to a string using json.dumps and then use the technique shown on this post to find and round your floating point numbers.

为了对其进行测试,我在您提供的示例之上添加了一些更复杂的嵌套结构:

To test it out, I added some more complicated nested structures on top of the example you provided::

d = dict()
d['val'] = 5.78686876876089075543
d['name'] = 'kjbkjbkj'
d["mylist"] = [1.23456789, 12, 1.23, {"foo": "a", "bar": 9.87654321}]
d["mydict"] = {"bar": "b", "foo": 1.92837465}

# dump the object to a string
d_string = json.dumps(d, indent=4)

# find numbers with 8 or more digits after the decimal point
pat = re.compile(r"\d+\.\d{8,}")
def mround(match):
    return "{:.7f}".format(float(match.group()))

# write the modified string to a file
with open('test.json', 'w') as f:
    f.write(re.sub(pat, mround, d_string))

输出test.json如下:

{
    "val": 5.7868688,
    "name": "kjbkjbkj",
    "mylist": [
        1.2345679,
        12,
        1.23,
        {
            "foo": "a",
            "bar": 9.8765432
        }
    ],
    "mydict": {
        "bar": "b",
        "foo": 1.9283747
    }
}

此方法的一个局限性在于它也将匹配双引号内的数字(以字符串表示的浮点数).根据您的需要,您可以提出一个限制性更强的正则表达式来处理此问题.

One limitation of this method is that it will also match numbers that are within double quotes (floats represented as strings). You could come up with a more restrictive regex to handle this, depending on your needs.

以下内容将适用于您的示例并处理您将遇到的大多数极端情况:

Here is something that will work on your example and handle most of the edge cases you will encounter:

import json

class MyCustomEncoder(json.JSONEncoder):
    def iterencode(self, obj):
        if isinstance(obj, float):
            yield format(obj, '.7f')
        elif isinstance(obj, dict):
            last_index = len(obj) - 1
            yield '{'
            i = 0
            for key, value in obj.items():
                yield '"' + key + '": '
                for chunk in MyCustomEncoder.iterencode(self, value):
                    yield chunk
                if i != last_index:
                    yield ", "
                i+=1
            yield '}'
        elif isinstance(obj, list):
            last_index = len(obj) - 1
            yield "["
            for i, o in enumerate(obj):
                for chunk in MyCustomEncoder.iterencode(self, o):
                    yield chunk
                if i != last_index: 
                    yield ", "
            yield "]"
        else:
            for chunk in json.JSONEncoder.iterencode(self, obj):
                yield chunk

现在使用自定义编码器写入文件.

Now write the file using the custom encoder.

with open('test.json', 'w') as f:
    json.dump(d, f, cls = MyCustomEncoder)

输出文件test.json:

{"val": 5.7868688, "name": "kjbkjbkj", "mylist": [1.2345679, 12, 1.2300000, {"foo": "a", "bar": 9.8765432}], "mydict": {"bar": "b", "foo": 1.9283747}}


为了使诸如indent之类的其他关键字参数起作用,最简单的方法是读入刚刚写入的文件,然后使用默认编码器将其写回:


In order to get other keyword arguments like indent to work, the easiest way would be to read in the file that was just written and write it back out using the default encoder:

# write d using custom encoder
with open('test.json', 'w') as f:
    json.dump(d, f, cls = MyCustomEncoder)

# load output into new_d
with open('test.json', 'r') as f:
    new_d = json.load(f)

# write new_d out using default encoder
with open('test.json', 'w') as f:
    json.dump(new_d, f, indent=4)

现在输出文件与选项1所示相同.

Now the output file is the same as shown in option 1.

这篇关于如何限制JSONEncoder产生的浮点数的数量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆