【问题标题】:2.2GB JSON file parses inconsistently2.2GB JSON 文件解析不一致
【发布时间】:2013-10-13 11:53:48
【问题描述】:

我正在尝试解码一个大的 utf-8 json 文件 (2.2 GB)。我像这样加载文件:

f = codecs.open('output.json', encoding='utf-8')
data = f.read()

如果我尝试执行以下任一操作:json.loadjson.loadsjson.JSONDecoder().raw_decode,我会收到错误消息:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-40-fc2255017b19> in <module>()
----> 1 j = jd.decode(data)

/usr/lib/python2.7/json/decoder.pyc in decode(self, s, _w)
    367         end = _w(s, end).end()
    368         if end != len(s):
--> 369             raise ValueError(errmsg("Extra data", s, end, len(s)))
    370         return obj
    371

ValueError: Extra data: line 1 column -2065998994 - line 1 column 2228968302
    (char -2065998994 - 2228968302)


uname -m 显示 x86_64

> python -c 'import sys;print("%x" % sys.maxsize, sys.maxsize > 2**32)'
('7fffffffffffffff', True)`

所以我应该是 64 位的,整数大小应该不是问题。

但是,如果我运行:

jd = json.JSONDecoder()
len(data) # 2228968302
j = jd.raw_decode(data)
j[1] # 2228968302 

raw_decode 返回的元组中的第二个值是字符串的结尾,所以raw_decode 似乎解析了整个文件,结尾似乎没有垃圾。

那么,我应该对 json 做些不同的事情吗? raw_decode 是否真的在解码整个文件?为什么json.load(s) 失败了?

【问题讨论】:

  • 你在什么系统上运行? 22 亿对于带符号的 32 位整数来说太大了,异常详细信息中的负数表明您遇到了问题。
  • 如果不查看底层代码,我会猜测这些函数将输入转换为字符串,并且在尝试处理该大小的字符串时会出现溢出问题。 “原始”版本可能没有,因此能够解析整个事情。
  • @TimPeters 我将此添加到我的问题中,但我使用的是 64 位架构。
  • @TimPeters 是的,但是从 raw_decode 返回的索引指示解码文档的结尾是字符串的最后一个索引。
  • @MrFooz 我刚刚运行了一个调试器:s 是 unicode,end 是 int。

标签: python json unicode utf-8


【解决方案1】:

我会将此作为评论添加,但 cmets 中的格式化功能太有限了。

盯着源代码,

raise ValueError(errmsg("Extra data", s, end, len(s)))

调用这个函数:

def errmsg(msg, doc, pos, end=None):
    ...
    fmt = '{0}: line {1} column {2} - line {3} column {4} (char {5} - {6})'
    return fmt.format(msg, lineno, colno, endlineno, endcolno, pos, end)

格式的(char {5} - {6}) 部分是您显示的错误消息的这一部分:

(char -2065998994 - 2228968302)

所以,在errmsg() 中,pos 是 -2065998994,end 是 2228968302。看哪! ;-):

>>> pos = -2065998994
>>> end = 2228968302
>>> 2**32 + pos
2228968302L
>>> 2**32 + pos == end
True

也就是说,posend“确实”相同。从调用 errmsg() 的位置返回,这意味着 endlen(s) 也确实相同 - 但 end 被视为 32 位有符号整数。 end 又来自正则表达式匹配对象的end() 方法。

所以这里真正的问题似乎是正则表达式引擎中的 32 位限制/假设。我鼓励你open a bug report

稍后:回答您的问题,是的,raw_decode() 正在解码整个文件。其他方法调用 raw_decode(),但之后添加(失败!)健全性检查。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-04-07
    • 1970-01-01
    • 2018-10-08
    • 1970-01-01
    • 2016-06-19
    • 1970-01-01
    • 2021-07-14
    相关资源
    最近更新 更多