2017-07-11 2 views
-1

J'ai un fichier météo où je voudrais extraire la première valeur pour "air_temp" enregistré dans un fichier JSON. Le format utilisé par ce retriever HTTP est regex (je sais que ce n'est pas la meilleure méthode).Regex Retour Premier Match

J'ai raccourci le fichier JSON 2 entrées de données pour la simplicité - il y a généralement 100.

{ 
    "observations": { 
    "notice": [ 
     { 
     "copyright": "Copyright Commonwealth of Australia 2017, Bureau of Meteorology. For more information see: http://www.bom.gov.au/other/copyright.shtml http://www.bom.gov.au/other/disclaimer.shtml", 
     "copyright_url": "http://www.bom.gov.au/other/copyright.shtml", 
     "disclaimer_url": "http://www.bom.gov.au/other/disclaimer.shtml", 
     "feedback_url": "http://www.bom.gov.au/other/feedback" 
     } 
    ], 
    "header": [ 
     { 
     "refresh_message": "Issued at 12:11 pm EST Tuesday 11 July 2017", 
     "ID": "IDN60901", 
     "main_ID": "IDN60902", 
     "name": "Canberra", 
     "state_time_zone": "NSW", 
     "time_zone": "EST", 
     "product_name": "Capital City Observations", 
     "state": "Aust Capital Territory" 
     } 
    ], 
    "data": [ 
     { 
     "sort_order": 0, 
     "wmo": 94926, 
     "name": "Canberra", 
     "history_product": "IDN60903", 
     "local_date_time": "11/12:00pm", 
     "local_date_time_full": "20170711120000", 
     "aifstime_utc": "20170711020000", 
     "lat": -35.3, 
     "lon": 149.2, 
     "apparent_t": 5.7, 
     "cloud": "Mostly clear", 
     "cloud_base_m": 1050, 
     "cloud_oktas": 1, 
     "cloud_type_id": 8, 
     "cloud_type": "Cumulus", 
     "delta_t": 3.6, 
     "gust_kmh": 11, 
     "gust_kt": 6, 
     "air_temp": 9.0, 
     "dewpt": 0.2, 
     "press": 1032.7, 
     "press_qnh": 1031.3, 
     "press_msl": 1032.7, 
     "press_tend": "-", 
     "rain_trace": "0.0", 
     "rel_hum": 54, 
     "sea_state": "-", 
     "swell_dir_worded": "-", 
     "swell_height": null, 
     "swell_period": null, 
     "vis_km": "10", 
     "weather": "-", 
     "wind_dir": "WNW", 
     "wind_spd_kmh": 7, 
     "wind_spd_kt": 4 
     }, 
     { 
     "sort_order": 1, 
     "wmo": 94926, 
     "name": "Canberra", 
     "history_product": "IDN60903", 
     "local_date_time": "11/11:30am", 
     "local_date_time_full": "20170711113000", 
     "aifstime_utc": "20170711013000", 
     "lat": -35.3, 
     "lon": 149.2, 
     "apparent_t": 4.6, 
     "cloud": "Mostly clear", 
     "cloud_base_m": 900, 
     "cloud_oktas": 1, 
     "cloud_type_id": 8, 
     "cloud_type": "Cumulus", 
     "delta_t": 2.9, 
     "gust_kmh": 9, 
     "gust_kt": 5, 
     "air_temp": 7.3, 
     "dewpt": 0.1, 
     "press": 1033.1, 
     "press_qnh": 1031.7, 
     "press_msl": 1033.1, 
     "press_tend": "-", 
     "rain_trace": "0.0", 
     "rel_hum": 60, 
     "sea_state": "-", 
     "swell_dir_worded": "-", 
     "swell_height": null, 
     "swell_period": null, 
     "vis_km": "10", 
     "weather": "-", 
     "wind_dir": "NW", 
     "wind_spd_kmh": 4, 
     "wind_spd_kt": 2 
     } 
    ] 
    } 
} 

L'expression regex J'utilise actuellement est: .*air_temp": (\d+).* mais est de retour 9 et 7.3 (entrées 1 et 2). Quelqu'un pourrait-il suggérer un moyen de ne retourner que la première valeur?

J'ai essayé d'utiliser un groupe de quantificateurs paresseux, mais je n'ai pas eu de chance.

+0

Dans quelle langue programmez-vous? – mickmackusa

Répondre

0

Ce regex vous aidera. Mais je pense que vous devriez capturer et extraire le premier match avec les fonctionnalités du langage de programmation que vous utilisez.

.*air_temp": (\d{1,3}\.\d{0,3})[\s\S]*?}, 

Pour comprendre l'expression rationnelle mieux: jeter un oeil at this.

Mise à jour

La solution fonctionne ci-dessus si vous avez seulement deux entrées de données. Pour plus de deux entrées, nous aurions dû utiliser celui-ci:

header[\s\S]*?"air_temp": (\d{1,3}\.\d{0,3}) 

nous correspondent ici le mot header d'abord, puis correspondre à quelque chose d'une manière non-gourmand. Après cela, nous correspondons à notre modèle attendu. ainsi nous obtenons le premier match. Jouez avec ici au regex101.

Pour capturer les nombres négatifs, nous devons vérifier s'il existe un caractère - ou non. Nous faisons cela par ? ce qui signifie 'Le point d'interrogation indique zéro ou une occurrence de l'élément précédent'.

Ainsi, le regex devient,

header[\s\S]*?"air_temp": (-?\d{1,3}\.\d{0,3})Demo

Mais l'utilisation de \K sans le drapeau global (dans une autre réponse donnée par mickmackusa) est plus efficace. Pour détecter des nombres négatifs, la version modifiée de cette expression régulière est

air_temp": \K-?\d{1,2}\.\d{1,2}demo.

Ici {1,2} signifie 1 ~ 2 occurrences/s du caractère précédent.Nous utilisons cela comme {min_occurance,max_occurance}

+0

Cela fonctionne pour les nombres positifs! Mais avez-vous une suggestion à utiliser pour les valeurs négatives aussi? C'est à dire. si "air_temp" est -2? –

+0

@HenryBlue, la réponse a été mise à jour. – arif

0

Je ne sais pas quelle langue vous utilisez, mais cela semble être une différence entre le drapeau global et l'absence de drapeau global.

Si le drapeau global n'est pas défini, seul le premier résultat sera renvoyé. Si le drapeau global est défini sur votre regex, il va itérer en retournant tous les résultats possibles. Vous pouvez tester facilement en utilisant Regex101, https://regex101.com/r/x1bwg2/1

Le paresseux/greediness ne devrait pas avoir d'impact en ce qui concerne l'utilisation de/ne pas utiliser l'indicateur global

0

Si \K est autorisé dans votre langue codage, utilisez ceci: Demo

/air_temp": \K[\d.]+/ (117steps) ce sera très efficace dans la recherche de votre très grand texte JSON.

Si aucun \K est autorisé, vous pouvez utiliser un groupe de capture: (Demo)

/air_temp": ([\d.]+)/ cela encore passer à une vitesse décente à travers votre texte JSON

avis qu'il n'y a pas de drapeau global à la fin du motif, donc après un match, le moteur regex arrête de chercher.



Mise à jour:

Pour les matches "moins littérale" (mais il ne devrait pas question si votre source est fiable), vous pouvez utiliser:

classe de caractères étendu pour inclure - :

/air_temp": \K[\d.-]+/ #still 117 steps 

ou de modifier la classe de caractères et de correspondre à tout ce qui n'est pas un , (parce que la valeur se termine toujours par une virgule):

/air_temp": \K[^,]+/  #still 117 steps 

Pour un match très stricte (si vous êtes à la recherche d'un modèle qui signifie que vous avez ZERO confiance dans les données d'entrée) ...

Il semble que vos données ne va pas au-delà d'un chiffre après la virgule, entre temps 0 et 1 PREPEND une 0 avant la virgule, et je ne pense pas que vous devez vous inquiéter avec des températures dans les centaines (à droite ?), vous pouvez donc utiliser:

/air_temp": \K-?[1-9]?\d(?:\.\d)?  #200steps 

Explication:

  1. signe négatif en option
  2. dizaines option chiffres
  3. les requis chiffres
  4. décimal optionnel qui doit être suivi d'un chiffre

Accuracy Test Demo

Real Data Demo

+0

\ K est autorisé - merci. Bien que je ne considérais pas les valeurs de température négatives. Que suggérez-vous que j'utilise pour capturer -5 par exemple? –

+0

@HenryBlue J'ai mis à jour ma réponse avec quelques nouveaux motifs et explications.Vous devriez prendre un moment pour mettre à jour votre question avec vos nouvelles exigences afin que nos réponses aient un sens. Si vous avez des questions, il suffit de demander. – mickmackusa