Key=Value en regex, extraction en PowerShell

Key=Value en Regex

Salut à tous, aujourd’hui je vous propose un focus sur la capture de structures répétées type Key=Value en Regex (ou expression régulières). C’est pas bien compliqué mais il semble qu’il y en ai quelques uns qui ne soit pas au courant du comment faire (et pour ceux qui savent même pas les comment/quoi des regex c’est par ici).

Comme un bon exemple est toujours plus parlant voici le genre de structure qu’on veut extraire :

key1="valueA" key2="valueB" key3="valueC"

Celui qui débute avec les regex aura surement envie d’écrire une regex de ce type:

(key1=\".*\") (key2=\".*\") (key3=\".*\")

Sauf que cette expression de capture des Key=Value en Regex, en plus d’être peu optimisée, n’est pas généralisable. J’entends que si le texte à analyser ne contient pas toujours les mêmes clés et pas forcément le même nombre entre plusieurs lignes. Exemple où cela ne fonctionne plus :

key1="valueA" key2="valueB" key3="valueC"
key1="valueA" key3="valueB" key5="valueC" key0="valueC"
key2="valueB" key52="valueC"

Du coup, là, on va commencer à devoir faire de vraies expressions régulières.

Première optimisation

Déjà, il est souhaitable ne pas utiliser

(.*) # "n'importe quoi plusieurs fois"

mais lui préférer :

([^=]+)= #"Tout jusqu'à un caractère spécifique"

Deuxième optimisation

Plutôt que simplement des groupes de capture, il est plus pratique d’utiliser des groupes de capture nommés, exemple :

(?<cle>[^=]+)=

Troisième optimisation

Assez évidente utiliser une structure répétée (doc), plutôt que d’écrire :

(key1=\".*\") (key2=\".*\"), 

Il vaut mieux utiliser

((.*)=\"(.*)\")+?

Notez le +? à la fin, pour rendre le + « lazy » ce qui permet d’éviter que la regex aille matcher tout ce qui suit.

Key=Value en Regex

Au final, ça donne cette expression :

(?<full_kv>(?<key>[^=]+)=\"(?<value>[^\"]+)\")+?

extraction Key=Value en PowerShell ?

Voici un exemple se script simpliste pour sortir des objets PowerShell (PSCustomObject) de notre exemple de texte :

$Texte=@"
key1="valueA" key2="valueB" key3="valueC"
key1="valueA" key3="valueB" key5="valueC" key0="valueC"
key2="valueB" key52="valueC"
"@

$regex = [regex]::new('(?<full_kv>(?<key>[^=]+)=\"(?<value>[^\"]+)\")+?')

ForEach ($line in $($Texte -split "`r`n")) {
    $result = $regex.Matches($line)
    $dic = @{}
    ForEach ($Match in $result) {
        $KeyName = ($Match.Groups | Where-Object {$_.Name -eq "key"}).value
        $KeyValue = ($Match.Groups | Where-Object {$_.Name -eq "value"}).value
        $dic.add($KeyName,$KeyValue)
    } 
    [PSCustomObject] $dic 
}

Vous trouverez aussi le code source sur mon Github (avec les mises à jour éventuelles depuis aussi). Si vous cherchez des exemples de regex plus compliquées, je vous rappel que mes TP pour mes TA Splunk en contiennent des jolies aussi.

Bref, j’espère que ça vous a intéressé, bossez bien sur vos regex (avec Regex101) et @+.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.