web-dev-qa-db-fra.com

comment utiliser une expression régulière pour extraire des champs JSON?

Débutant RegExp question. J'ai des lignes de JSON dans un fichier texte, chacune avec des champs légèrement différents, mais il y a 3 champs que je veux extraire pour chaque ligne si elle en possède, en ignorant tout le reste. Comment pourrais-je utiliser un regex (dans editpad ou ailleurs) pour faire cela?

Exemple:

"url":"http://www.netcharles.com/orwell/essays.htm",
"domain":"netcharles.com",
"title":"Orwell Essays & Journalism Section - Charles' George Orwell Links",
"tags":["orwell","writing","literature","journalism","essays","politics","essay","reference","language","toread"],
"index":2931,
"time_created":1345419323,
"num_saves":24

Je veux extraire l'URL, le titre, les balises,

6
James Cooper
/"(url|title|tags)":"((\\"|[^"])*)"/i

Je pense que c'est ce que vous demandez. Je vais fournir une explication dans un instant. Cette expression régulière (délimitée par / - vous n'aurez probablement pas à les mettre dans le pavé de saisie) correspond:

"

Un " littéral.

(url|title|tags)

N'importe laquelle des trois chaînes littérales "url", "title" ou "tags" - dans les expressions rationnelles, les parenthèses sont utilisées par défaut pour créer des groupes, et le caractère de canal est utilisé pour alterner - comme un "ou" logique. Pour faire correspondre ces caractères littéraux, vous devez les échapper.

":"

Une autre chaîne littérale.

(

Le début d'un autre groupe. (Groupe 2)

    (

Un autre groupe (3)

        \\"

La chaîne littérale \" - vous devez échapper à la barre oblique inversée, sinon elle sera interprétée comme échappant au caractère suivant et vous ne saurez jamais ce que cela va faire.

        |

ou...

        [^"]

N'importe quel caractère, à l'exception des guillemets doubles. Les crochets indiquent une classe/un jeu de caractères ou une liste de caractères à comparer. Toute classe donnée correspond exactement à un caractère de la chaîne. L'utilisation d'un carat (^) au début d'une classe le nie, ce qui fait que l'adaptateur correspond à tout ce qui n'est pas contenu dans la classe.

    )

Fin du groupe 3 ...

    *

L'astérisque provoque la répétition de l'expression régulière précédente (dans ce cas, groupe 3), zéro ou plusieurs fois. Dans ce cas, le matcher correspond à tout ce qui pourrait se trouver entre les guillemets d'une chaîne JSON.

)"

La fin du groupe 2 et un " littéral.

J'ai fait quelques choses non évidentes ici, qui peuvent être utiles:

  1. Groupe 2 - lorsqu’il est déréférencé à l’aide de Backreferences - correspond à la chaîne attribuée au champ. Ceci est utile pour obtenir la valeur réelle.
  2. Le i à la fin de l'expression le rend insensible à la casse.
  3. Le groupe 1 contient le nom du champ capturé.

EDIT: Je vois donc que les tags sont un tableau. Je mettrai à jour l'expression régulière ici dans une seconde lorsque j'aurai eu l'occasion d'y réfléchir.

Votre nouvelle regex est:

/"(url|title|tags)":("(\\"|[^"])*"|\[("(\\"|[^"])*"(,"(\\"|[^"])*")*)?\])/i

Tout ce que j'ai fait ici est d'alterner l'expression régulière de chaîne que j'avais utilisée ("((\\"|[^"])*)"), avec une expression régulière permettant de rechercher des tableaux (\[("(\\"|[^"])*"(,"(\\"|[^"])*")*)?\]). Non si facile à lire, est-ce? Eh bien, en substituant notre String Regex à la lettre S, nous pouvons la réécrire comme suit:

\[(S(,S)*)?\]

Ce qui correspond à un crochet d’ouverture littéral (d’où les barres obliques inverses), suivi éventuellement d’une liste de chaînes séparées par des virgules et d’un crochet de fermeture. Le seul nouveau concept que j'ai introduit ici est le point d'interrogation (?), qui est lui-même un type de répétition. Communément appelé «rendant l'expression précédente facultative», il peut également être considéré comme une correspondance exacte égale à 0 ou 1.

Avec notre même notation S, voici l'intégralité de l'expression régulière sale:

/"(url|title|tags)":(S|\[(S(,S)*)?\])/i

Si cela aide de le voir en action, voici une vue de celui-ci en action.

16
FrankieTheKneeMan

Cette question est un peu plus ancienne, mais je suis allée un peu sur mon PC et j'ai trouvé cette expression. Je lui ai passé comme Gist, pourrait être utile à d'autres.

MODIFIER:

# Expression was tested with PHP and Ruby
# This regular expression finds a key-value pair in JSON formatted strings
# Match 1: Key
# Match 2: Value
# https://regex101.com/r/zR2vU9/4
# http://rubular.com/r/KpF3suIL10

(?:\"|\')(?<key>[^"]*)(?:\"|\')(?=:)(?:\:\s*)(?:\"|\')?(?<value>true|false|[0-9a-zA-Z\+\-\,\.\$]*)

# test document
[
  {
    "_id": "56af331efbeca6240c61b2ca",
    "index": 120000,
    "guid": "bedb2018-c017-429E-b520-696ea3666692",
    "isActive": false,
    "balance": "$2,202,350",
    "object": {
        "name": "am",
        "lastname": "lang"
    }
  }
]
2
creep3007

Pourquoi doit-il s'agir d'un objet d'expression régulière?

Ici, nous pouvons simplement utiliser d’abord un objet Hash, puis le rechercher.

mh = {"url":"http://www.netcharles.com/orwell/essays.htm","domain":"netcharles.com","title":"Orwell Essays & Journalism Section - Charles' George Orwell Links","tags":["orwell","writing","literature","journalism","essays","politics","essay","reference","language","toread"],"index":2931,"time_created":1345419323,"num_saves":24}

Dont la sortie serait

=> {:url=>"http://www.netcharles.com/orwell/essays.htm", :domain=>"netcharles.com", :title=>"Orwell Essays & Journalism Section - Charles' George Orwell Links", :tags=>["orwell", "writing", "literature", "journalism", "essays", "politics", "essay", "reference", "language", "toread"], :index=>2931, :time_created=>1345419323, :num_saves=>24}

Non pas que je veuille éviter d’utiliser Regexp, mais ne pensez-vous pas qu’il serait plus facile de procéder étape par étape jusqu’à ce que vous obteniez les données que vous souhaitez approfondir? Juste MHO.

mh.values_at(:url, :title, :tags)

Le résultat:

["http://www.netcharles.com/orwell/essays.htm", "Orwell Essays & Journalism Section - Charles' George Orwell Links", ["orwell", "writing", "literature", "journalism", "essays", "politics", "essay", "reference", "language", "toread"]]

En prenant le modèle que FrankieTheKneeman vous a donné:

pattern = /"(url|title|tags)":"((\\"|[^"])*)"/i

nous pouvons rechercher le hachage mh en le convertissant en un objet json.

/#{pattern}/.match(mh.to_json)

Le résultat:

=> #<MatchData "\"url\":\"http://www.netcharles.com/orwell/essays.htm\"" 1:"url" 2:"http://www.netcharles.com/orwell/essays.htm" 3:"m">

Bien sûr, tout cela se fait en Ruby, ce qui n’est pas une étiquette que vous avez, mais une relation que j’espère.

Mais oups! On dirait que nous ne pouvons pas faire tous les trois à la fois avec ce motif, alors je les ferai un à la fois, juste pour l'amour.

pattern = /"(title)":"((\\"|[^"])*)"/i

/#{pattern}/.match(mh.to_json)

#<MatchData "\"title\":\"Orwell Essays & Journalism Section - Charles' George Orwell Links\"" 1:"title" 2:"Orwell Essays & Journalism Section - Charles' George Orwell Links" 3:"s">

pattern = /"(tags)":"((\\"|[^"])*)"/i

/#{pattern}/.match(mh.to_json)

=> nil

Désolé pour ce dernier. Cela devra être traité différemment.

1
Douglas G. Allen

J'ai adapté regex pour travailler avec JSON dans ma propre bibliothèque. J'ai détaillé le comportement de l'algorithme ci-dessous.

Tout d'abord, stringify l'objet JSON. Ensuite, vous devez stocker les débuts et les longueurs des sous-chaînes correspondantes. Par exemple:

"matched".search("ch") // yields 3

Pour une chaîne JSON, cela fonctionne exactement de la même manière (à moins que vous ne recherchiez explicitement des virgules et des accolades, auquel cas je recommanderais une transformation préalable de votre objet JSON avant d'effectuer une expression régulière (c'est-à-dire think:, {,}).

Ensuite, vous devez reconstruire l'objet JSON. L'algorithme que j'ai créé le fait en détectant la syntaxe JSON en remontant récursivement de l'index de correspondance. Par exemple, le pseudo-code pourrait ressembler à ceci:

find the next key preceding the match index, call this theKey
then find the number of all occurrences of this key preceding theKey, call this theNumber
using the number of occurrences of all keys with same name as theKey up to position of theKey, traverse the object until keys named theKey has been discovered theNumber times
return this object called parentChain

Avec ces informations, il est possible d'utiliser regex pour filtrer un objet JSON afin de renvoyer la clé, la valeur et la chaîne d'objet parent.

Vous pouvez voir la bibliothèque et le code que j'ai créés à http://json.spiritway.co/

0
mikewhit