【问题标题】:Unable to parse TAB in JSON files无法解析 JSON 文件中的 TAB
【发布时间】:2013-11-16 22:02:18
【问题描述】:

我在加载似乎包含 TAB 字符的 JSON 文件时遇到了解析问题。

当我转到http://jsonlint.com/,并输入带有TAB字符的部分时:

{
    "My_String": "Foo bar.  Bar foo."
}

验证器抱怨:

Parse error on line 2:
{    "My_String": "Foo bar. Bar foo."
------------------^
Expecting 'STRING', 'NUMBER', 'NULL', 'TRUE', 'FALSE', '{', '['

这实际上是有问题的 JSON 文本的复制/粘贴。

我尝试使用jsonsimplejson 加载此文件,但没有成功。我怎样才能正确加载它?我应该只预处理文件并将 TAB 替换为\t 还是空格?还是我在这里遗漏了什么?

更新:

这里也是simplejson中的一个有问题的例子:

foo = '{"My_string": "Foo bar.\t Bar foo."}'
simplejson.loads(foo)

JSONDecodeError: Invalid control character '\t' at: line 1 column 24 (char 23)

【问题讨论】:

    标签: python json


    【解决方案1】:

    来自JSON standard

    在任何标记之前或之后都允许有无关紧要的空格。这 空白字符是:字符列表 (U+0009)、换行 (U+000A)、回车 (U+000D) 和空格 (U+0020)。空格是 不允许在任何令牌中,除非允许空间 字符串。

    这意味着在 JSON 字符串中不允许使用文字制表符。您需要将其转义为 \t (in a .json-file):

    {"My_string": "Foo bar.\t Bar foo."}
    

    此外,如果在 Python 字符串文字中提供了 json 文本,那么您需要对制表符进行双重转义:

    foo = '{"My_string": "Foo bar.\\t Bar foo."}' # in a Python source
    

    或者使用 Python 原始字符串文字:

    foo = r'{"My_string": "Foo bar.\t Bar foo."}' # in a Python source
    

    【讨论】:

    • 谢谢!我对"Whitespace is not allowed within any token" 的声明和您的总体报价感到有些困惑。字符串"Foo bar.\t Bar foo." 中有空格。您如何从该引用中推断出允许文字空格,但不允许文字制表符?
    • @Josh:阅读全文:“除了字符串中允许有空格”.
    • 天哪,明白了。谢谢!
    【解决方案2】:

    制表符在值之外作为分隔空格是合法的,但在字符串内则不是。请改用\t

    编辑:根据您的 cmets,我看到一些关于标签实际上是什么的困惑.. 制表符只是一个普通字符,如“a”或“5”或“。”或您通过按键盘上的键输入的任何其他字符。它占用一个字节,其数值为 9。不涉及反斜杠或小写 't。

    标签与“a”、“5”或“.”不同的类别是什么?事实上,作为一个使用眼球的人,您通常无法查看文本显示并识别或计算制表符字符。在视觉上,一系列制表符与一系列(通常较大但在视觉上仍不确定的)空格序列相同。

    为了在用于计算机处理的文本中明确表示选项卡,我们有多种句法方法可以说“嘿,某个软件!稍后用制表符替换这个垃圾,好吗?” .

    在编程语言的历史上,主要有两种方法;如果你回到 1950 年代,你会发现这两种方法并存,在两种最古老的高级语言中各有一种。 Lisp 已将字符文字命名为 #\Tab;一旦从程序源中读取它们,它们就会被转换。 Fortran 只有CHAR 函数,该函数在运行时调用并返回编号与参数匹配的字符:CHAR(9) 返回一个制表符。 (当然,如果它真的是 CHAR(9) 而不是 CHAR(某个表达式可以达到 9),优化编译器可能会注意到这一点,并在编译时用选项卡替换函数调用时间,让我们回到另一个营地。)

    一般来说,对于这两种解决方案类型,如果您想将特殊字符粘贴到较大的字符串中,您必须自己进行连接;例如,一个 80 年代 hack BASIC 的孩子可能会这样写:

    10 PRINT "This is a tab ->"; CHR$(9); "<- That was a tab"
    

    但有些语言——尤其是以语言 B 开头的家族——引入了将这些字符直接包含在字符串文字中的能力:

    printf("This is a tab -> *t <- That was a tab");
    

    BCPL 保留了 * 语法,但该系列中的下一个语言 C 将其替换为反斜杠,可能是因为他们需要更频繁地读写文字星号而不是文字反斜杠。

    不管怎样,很多语言,包括 Python 和 Javascript,都在这里借用或继承了 C 的约定。因此,在这两种语言中,"\t"'\t' 这两个表达式都会产生一个单字符字符串,其中一个字符是制表符。

    JSON 基于 Javascript 的语法,但它只允许它的一个受限子集。例如,字符串必须用双引号 (") 而非单引号 (') 括起来,并且其中不允许有文字制表符。

    这意味着您更新中的这个 Python 字符串:

    foo = '{"My_string": "Foo bar.\t Bar foo."}'
    

    不是有效的 JSON。 Python 解释器在读取字符串后立即将\t 序列转换为实际的制表符 - 远在 JSON 处理器看到它之前。

    你可以告诉 Python 在字符串中加入文字 \t 而不是制表符,方法是加倍反斜杠:

    foo = '{"My_string": "Foo bar.\\t Bar foo."}'
    

    或者您可以使用“原始”字符串语法,它根本不解释特殊的反斜杠序列:

    foo = r'{"My_string": "Foo bar.\t Bar foo."}'
    

    无论哪种方式,JSON 处理器都会看到一个包含反斜杠后跟“t”的字符串,而不是包含制表符的字符串。

    【讨论】:

    • 嗯。有趣的。将有问题的tab 替换为\t 通过在线JSONlint 验证器,但simplejson 抱怨:JSONDecodeError: Invalid control character '\t' at: line XXX column YYY (char ZZZ)
    • JSONDecodeError 具有误导性。它发现了一个无效的文字制表符。为了显示错误消息,它被翻译为 '\t' 而不是在屏幕上显示文字标签。
    【解决方案3】:

    您可以通过对 JSON 文件进行转义来将制表符 包含在 值中(而不是空格)。这是 Python2.7 中 json 模块的一个工作示例:

    >>> import json
    >>> obj = json.loads('{"MY_STRING": "Foo\\tBar"}')
    >>> obj['MY_STRING']
    u'Foo\tBar'
    >>> print obj['MY_STRING']
    Foo    Bar
    

    虽然不转义 '\t' 会导致错误:

    >>> json.loads('{"MY_STRING": "Foo\tBar"}')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/__init__.py", line 338, in loads
        return _default_decoder.decode(s)
      File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/decoder.py", line 365, in decode
        obj, end = self.raw_decode(s, idx=_w(s, 0).end())
      File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/decoder.py", line 381, in raw_decode
        obj, end = self.scan_once(s, idx)
    ValueError: Invalid control character at: line 1 column 19 (char 18)
    

    【讨论】:

    • 谢谢mtitan8。您知道官方 JSON 规范中是否提到/讨论了这个问题?还是特定于库如何解析文件?
    • 乔希,您的问题暗示存在问题,但没有问题。如果您使用简单的“\t”,那么 Python 会将其解释为文字制表符,这在 JSON 值中无效。因此,您将其转义为“\\t”,从而生成 Python 值“\t”,该值随后是有效的 JSON。有效字符在json.org的右侧
    • 可以使用 Python 原始文字:foo = r'{"My_string": "Foo bar.\t Bar foo."}'
    • 谢谢@AronGriffis。只是让我澄清一下:您是说在 JSON file 中包含未转义的 \t 字符在 JSON 中是合法的吗?其他人将这些文件写入磁盘,我正在尝试查看此问题是否是由于 them 未正确写入 JSON 文件,还是 I 方式存在问题解析文件。我可以看到无论如何都有一个简单的解决方法。
    • 文字制表符是 ASCII 9。这类似于 ASCII 10 的换行符,但通常在字符串(和 JSON)中表示为 \n。编写这些文件的人在字符串值中使用文字制表符编写它们。
    【解决方案4】:

    只是为了分享我的经验:

    我正在使用snakemake 和一个用Json 编写的配置文件。 json 文件中有用于缩进的制表符。为此目的,TAB 是合法的。但我收到错误消息:snakemake.exceptions.WorkflowError:配置文件不是有效的 JSON 或 YAML。我相信这是snakemake的错误;但我可能是错的。请评论。将所有 TAB 替换为空格后,错误消息就消失了。

    【讨论】:

      【解决方案5】:

      在节点红色流中我面临相同类型的问题:

      flow.set("delimiter",'"\t"');
      

      错误:

      { "status": "ERROR", "result": "Cannot parse config: String: 1: in value for key 'delimiter': JSON does not allow unescaped tab in quoted strings, use a backslash escape" }  
      

      解决方案:

      我只在代码中添加了\\t

       flow.set("delimiter",'"\\t"');
      

      【讨论】:

      • 这适用于节点红色流和 JSON 格式
      猜你喜欢
      • 1970-01-01
      • 2018-10-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-20
      相关资源
      最近更新 更多