À des fins de configuration, si je stocke une expression régulière "facile" dans un fichier JSON et que je la charge dans mon programme Python, cela fonctionne très bien.

{
    "allow": ["\/word\/.*"],
    "follow": true
},

Si je stocke une expression régulière plus complexe dans un fichier JSON, le même programme Python échoue.

{
    "allow": ["dcp\=[0-9]+\&dppp\="],
    "follow": true
},

C'est le code qui charge mon fichier JSON:

src_json = kw.get('src_json') or 'sources/sample.json'
self.MY_SETTINGS = json.load(open(src_json))

Et l'erreur est généralement la même, pointant mes recherches en ligne sur le fait que les expressions régulières ne doivent pas être stockées dans JSON des dossiers.

json.decoder.JSONDecodeError: Invalid \escape: line 22 column 38 (char 801)

Les fichiers YAML semblent avoir des limitations similaires, donc je ne devrais pas y remédier comme je suppose.

Maintenant, j'ai stocké mon expression dans un dict dans un fichier séparé:

mydict = {"allow": "com\/[a-z]+(?:-[a-z]+)*\?skid\="}

Et chargez-le depuis mon fichier programme:

exec(compile(source=open('expr.py').read(), filename='expr.py', mode='exec'))

print(mydict)

Ce qui fonctionne et serait bien avec moi - mais ça a l'air un peu ... spécial ... avec exec et compile.

Y a-t-il une raison de ne pas le faire de cette façon? Existe-t-il un meilleur moyen de stocker des structures de données complexes et des expressions régulières dans des fichiers externes que je peux ouvrir / utiliser dans mon code de programme?

2
Chris 27 janv. 2019 à 16:40

3 réponses

Meilleure réponse

Tout d'abord, les expressions régulières peuvent être stockées en JSON, mais doivent être stockées en JSON valide. C'est la cause de votre JSONDecodeError dans l'exemple.

Il y a d'autres réponses ici sur SO, qui expliquent comment encoder / décoder correctement l'expression régulière en tant que JSON valide, telles que: Échapper à Regex pour obtenir un JSON valide

Maintenant, les autres parties de votre question commencent à entrer dans plus de meilleures pratiques et opinions.

Comme vous l'avez vu, vous pouvez certainement déclarer et utiliser des variables à partir d'autres fichiers:

test_regex.py

my_dict = {'allow': 'com\\/[a-z]+(?:-[a-z]+)*\\?skid\\='}

script.py

from test_regex import mydict
mydict
{'allow': 'com\\/[a-z]+(?:-[a-z]+)*\\?skid\\='}

Cependant, c'est un cas d'utilisation de sentiment assez différent. Dans notre exemple JSON, les informations sont définies d'une manière que nous attendons à ce qu'elles soient plus facilement configurables - différents fichiers JSON pourraient être utilisés (peut-être pour différentes configurations d'environnement) chacun avec des expressions rationnelles différentes. Dans cet exemple, nous n'assumons pas la configurabilité mais à la place le test_regex est utilisé pour la séparation des problèmes et la lisibilité.

1
kyle 27 janv. 2019 à 14:20

Le lien que vous indiquez est la spécification JSON. Pour autant que je sache, cela ne dit rien sur les expressions régulières.

Ce que vous semblez faire, c'est prendre une expression régulière de travail et la coller dans votre fichier JSON pour (ré) utiliser. Et cela ne fonctionne pas toujours parce que certaines choses doivent être échappées pour que le JSON soit valide.

Il existe cependant un moyen simple d'insérer l'expression régulière dans le fichier JSON, avec les échappements appropriés , en créant un petit programme Python qui prendra l'expression régulière comme paramètre de ligne de commande, puis {{X0} } le fichier JSON, ou, alternativement, charger-mettre à jour-vider le fichier avec la nouvelle expression régulière.

2
Anthon 27 janv. 2019 à 13:59

Si vous stockez votre dictionnaire dans un fichier .py, vous pouvez importer la variable directement tant que le fichier se trouve dans votre PYTHONPATH ou vous utilisez un importation relative.

Par exemple, si je crée un fichier .py appelé expr.py et PYTHONPATH inclut le dossier dans lequel il se trouve.

Le contenu du fichier (identique à votre exemple):

mydict = {"allow": "com\/[a-z]+(?:-[a-z]+)*\?skid\="}

Ensuite, je peux l'exécuter à partir d'un interprète ou d'un autre script

>>> from expr import mydict
>>> mydict
{'allow': 'com\\/[a-z]+(?:-[a-z]+)*\\?skid\\='}

Pas besoin de jouer avec open() et exec sauf si je manque quelque chose ici. J'utilise cette approche pour stocker des expressions régulières, car vous pouvez stocker des objets re.compile directement.

Si je change le fichier en:

import re
mydict = {"allow": re.compile(r"com\/[a-z]+(?:-[a-z]+)*\?skid\=")}

Je peux faire:

>>> from expr import mydict
>>> print(mydict)
{'allow': re.compile('com\\/[a-z]+(?:-[a-z]+)*\\?skid\\=')}
>>> print(mydict["allow"].pattern)
com\/[a-z]+(?:-[a-z]+)*\?skid\=
>>> print(mydict["allow"].match("com/x-x?skid="))
<_sre.SRE_Match object; span=(0, 13), match='com/x-x?skid='>

Si le fichier contient une quantité stupide de regex, le tri automatique des variables sous le nom du script pourrait également aider à l'organisation:

Fichier:

import re
mydict = {"allow": re.compile(r"com\/[a-z]+(?:-[a-z]+)*\?skid\=")}
easydict = {"allow": re.compile(r"\/word\/.*"), "follow": True}
complexdict = {"allow": re.compile(r"dcp\=[0-9]+\&dppp\="), "follow": True}

Interprète:

>>> import expr
>>> print(expr.easydict["allow"].pattern)
\/word\/.*
>>> print(expr.complexdict["allow"].match("dcp=11&dppp="))
<_sre.SRE_Match object; span=(0, 12), match='dcp=11&dppp='>
>>> print(expr.mydict)
{'allow': re.compile('com\\/[a-z]+(?:-[a-z]+)*\\?skid\\=')}
0
Zhenhir 27 janv. 2019 à 14:44