Je rencontre un problème d’analyse lors du chargement de fichiers JSON qui semblent contenir le caractère TAB.
Quand je vais à http://jsonlint.com/ , et que j'entre la partie avec le caractère de tabulation:
{
"My_String": "Foo bar. Bar foo."
}
Le validateur se plaint de:
Parse error on line 2:
{ "My_String": "Foo bar. Bar foo."
------------------^
Expecting 'STRING', 'NUMBER', 'NULL', 'TRUE', 'FALSE', '{', '['
Il s'agit littéralement d'un copier/coller du texte JSON incriminé.
J'ai essayé de charger ce fichier avec json
et simplejson
sans succès. Comment puis-je charger cela correctement? Dois-je simplement prétraiter le fichier et remplacer TAB par \t
ou par un espace? Ou y a-t-il quelque chose qui me manque ici?
Voici également un exemple problématique dans simplejson
:
foo = '{"My_string": "Foo bar.\t Bar foo."}'
simplejson.loads(foo)
JSONDecodeError: Invalid control character '\t' at: line 1 column 24 (char 23)
De standard JSON :
Les espaces non significatifs sont autorisés avant ou après tout jeton. Le Les caractères d'espacement sont les suivants: tabulation de caractères (U + 0009), saut de ligne (U + 000A), retour chariot (U + 000D) et espace (U + 0020). L'espace blanc est non autorisé dans les jetons, sauf que l'espace est autorisé dans des cordes.
Cela signifie qu'un caractère de tabulation littéral n'est pas autorisé dans une chaîne JSON. Vous devez l’échapper en tant que \t
(dans un fichier .json) :
{"My_string": "Foo bar.\t Bar foo."}
De plus, si du texte json est fourni dans un littéral de chaîne Python, vous devez échapper deux fois à l'onglet
foo = '{"My_string": "Foo bar.\\t Bar foo."}' # in a Python source
Ou utilisez un littéral de chaîne brute Python:
foo = r'{"My_string": "Foo bar.\t Bar foo."}' # in a Python source
Les tabulations sont légales comme délimitant des espaces en dehors des valeurs, mais pas dans des chaînes. Utilisez \t
à la place.
EDIT: Sur la base de vos commentaires, je vois une certaine confusion sur ce qu'est une tabulation ... ... le caractère de tabulation est juste un caractère normal, comme "a" ou "5" ou "." ou tout autre caractère que vous entrez en appuyant sur une touche de votre clavier. Il occupe un seul octet, dont la valeur numérique est 9. Il n'y a pas de barres obliques inverses ni de minuscules.
Ce qui met l'onglet dans une catégorie différente de 'a' ou '5' ou '.' C’est le fait que, en tant qu’être humain utilisant vos globes oculaires, vous ne pouvez généralement pas afficher un texte et identifier ou compter des caractères de tabulation. Visuellement, une séquence de tabulations est identique à une séquence d'espaces (un nombre généralement plus grand mais toujours indéterminé visuellement).
Afin de représenter sans ambiguïté des tabulations dans un texte destiné à un traitement informatique, nous avons différentes méthodes syntaxiques pour dire "Hey, un logiciel! Remplacez cette ordure par un caractère de tabulation plus tard, OK?".
Dans l'histoire des langages de programmation, il y a eu deux approches principales; Si vous remontez dans les années 50, les deux approches coexistent, l'une dans deux des plus anciens langages de haut niveau. LISP avait nommé des littéraux de caractères tels que #\Tab
; ceux-ci ont été convertis dès qu'ils ont été lus à partir de la source du programme. Fortran ne possédait que la fonction CHAR
, appelée à l'exécution et renvoyant le caractère dont le numéro correspond à l'argument: CHAR(9)
a renvoyé une tabulation. (Bien sûr, si c'était vraiment CHAR(9)
et pas CHAR(
une expression qui fonctionne à 9)
, un compilateur optimiseur pourrait le remarquer et remplacer l'appel de fonction par un onglet au moment de la compilation, nous replaçant dans l'autre camp .)
En général, avec les deux types de solutions, si vous voulez insérer le caractère spécial dans une chaîne plus grande, vous devez effectuer la concaténation vous-même. Par exemple, un enfant qui pirate BASIC dans les années 80 pourrait écrire quelque chose comme ceci:
10 PRINT "This is a tab ->"; CHR$(9); "<- That was a tab"
Mais certaines langues - notamment la famille qui a commencé par le langage B - ont introduit la possibilité d'inclure ces caractères directement dans un littéral de chaîne:
printf("This is a tab -> *t <- That was a tab");
BCPL a conservé la syntaxe *
, mais le langage suivant de la série, C, l’a remplacée par la barre oblique inverse, probablement parce qu’ils devaient lire et écrire des astérisques littéraux beaucoup plus souvent que des barres obliques inverses littérales.
Quoi qu'il en soit, de nombreux langages, notamment Python et Javascript, ont emprunté ou hérité les conventions de C ici. Ainsi, dans les deux langues, les deux expressions "\t"
et '\t'
donnent chacune une chaîne de un caractère où ce caractère est une tabulation.
JSON est basé sur la syntaxe Javascript, mais il n'en autorise qu'un sous-ensemble restreint. Par exemple, les chaînes doivent être placées entre guillemets ("
) au lieu de guillemets simples ('
), et les tabulations littérales ne sont pas autorisées à l'intérieur.
Cela signifie que cette chaîne Python de votre mise à jour:
foo = '{"My_string": "Foo bar.\t Bar foo."}'
n'est pas valide JSON. L'interpréteur Python transforme la séquence \t
en un caractère de tabulation réel dès qu'il lit la chaîne, bien avant que le processeur JSON ne la voie.
Vous pouvez dire à Python de mettre un \t
littéral dans la chaîne au lieu d'un caractère de tabulation en doublant la barre oblique inverse:
foo = '{"My_string": "Foo bar.\\t Bar foo."}'
Ou vous pouvez utiliser la syntaxe de chaîne "brute", qui n'interprète pas du tout les séquences spéciales de barres obliques inverses:
foo = r'{"My_string": "Foo bar.\t Bar foo."}'
Dans les deux cas, le processeur JSON verra une chaîne contenant une barre oblique inversée suivie d'un «t», plutôt qu'une chaîne contenant un onglet.
Vous pouvez inclure des tabulations dans values (au lieu d'espaces) dans des fichiers JSON en les évitant. Voici un exemple de travail avec le module json
dans Python2.7:
>>> import json
>>> obj = json.loads('{"MY_STRING": "Foo\\tBar"}')
>>> obj['MY_STRING']
u'Foo\tBar'
>>> print obj['MY_STRING']
Foo Bar
Bien que ne pas échapper le '\t'
provoque une erreur:
>>> 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)
Dans le flux noeud-rouge, je suis confronté au même type de problème:
flow.set("delimiter",'"\t"');
Erreur:
{ "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" }
Solution:
j'ai ajouté seulement \\t
dans le code.
flow.set("delimiter",'"\\t"');
Juste pour partager mon expérience:
J'utilise snakemake et un fichier de configuration écrit en Json. Il y a des onglets dans le fichier json pour l'indentation. Les onglets sont légaux à cet effet. Mais je reçois un message d'erreur: snakemake.exceptions.WorkflowError: Le fichier de configuration n'est pas valide JSON ou YAML. Je crois que c'est un bug de snakemake; mais je peux me tromper. Commentez s'il vous plaît. Après avoir remplacé tous les onglets par des espaces, le message d'erreur a disparu.