Question [Fonction] Convert-BlockStructuredFile

Plus d'informations
il y a 5 ans 8 mois #18355 par Laurent Dardenne
Voici une fonction de conversion d'enregistrements structurés sur plusieurs ligne.
Le code découpage des enregistrement est externe à la fonction, ainsi on peut traiter différent type de structure.
[code:1]
Function Convert-BlockStructuredFile {
<#
.SYNOPSIS
Converti un fichier contenant des enregistrements, défini sur plusieurs lignes, en objets Powershell.
Chaque enregistrement doit contenir le même nombre de ligne.
Chaque ligne est transformée en une propriété d'objet, sauf les lignes vides.
#>
[CmdletBinding()]
Param (
#Ligne d'un enregistrement à analyser
[Parameter(Mandatory=$true,ValueFromPipeline = $true)]
[ValidateNotNullOrEmpty()]
[System.Management.Automation.PSObject] $InputObject,

#Nombre de ligne regroupant la définition d'un enregistrement.
#Sert à délimiter chaque enregistrement.
[Parameter(Mandatory=$true)]
[int] $LinePerBloc,

#Tableau des propriétés d'un enregistrement
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string[]] $Property,

#Code d'analyse de chaque ligne d'un enregistrement
#Le code doit pouvoir être utilisé en tant que filtre (fonction de type Filter)
[Parameter(Mandatory=$true)]
[ScriptBlock] $Split
)

begin {
$PropertiesCount=$Property.Count
$Elements=New-Object System.Collections.ArrayList($LinePerBloc)

function ConvertBlockStructuredFile {
$CurrentObject= New-Object PSObject
#Transforme un bloc en un objet Powershell
$Elements|
Foreach -process $Split| #Equivalent à un filtre
Foreach {
$CurrentObject.psObject.Properties.Add($_)
}

#ETS pouvant ajouter des membres dynamiquement,
# on contrôle uniquement les propriétés déclarées dans le bloc de texte
$IdenticalProperty=Compare-Object $Properties ($CurrentObject.PsObject.Properties|Select-Object -expand Name) -IncludeEqual -ExcludeDifferent|
Select-Object -expand InputObject

#cas : le nombre de propriété de l'objet ne correspond pas à la liste attendue.
if ($PropertiesCount -ne $IdenticalProperty.Count)
{
$ofs=','
#Affiche le détail de l'objet et la liste des propriétés manquantes
Write-Error \"Incomplete object : $CurrentObject `r`n Missing properties : $(Compare-Object $properties $IdenticalProperty|Select-Object -expand InputObject)\"
}
Write-Output $CurrentObject
$Elements.Clear() # Objet suivant
}#ConvertBlockStructuredFile
}#begin

process {
[void]$Elements.Add($InputObject)

#Regroupe l'enregistrement par bloc de n Lignes
if ($Elements.Count -eq $LinePerBloc)
{ ConvertBlockStructuredFile }
}#process

end {
#cas : Le dernier bloc peut ne pas se terminer par une ligne vide
if ($Elements.Count -ne 0)
{ ConvertBlockStructuredFile }
}#end
} #Convert-BlockStructuredFile
[/code:1]
Un fichier d'exemple :
[code:1]
$file ='c:\temp\datas.txt'
@'
[10]
nom=mon_nom
prenom=mon_prenom
code=1111
adresse=mon_adresse
postal=12345
sexe=m
tel=1234567890
etage=1/1
dpt=bloc <10>

[12]
nom=mon_nom1
prenom=mon_prenom1
code=2222
adresse=mon_adresse1
postal=56789
sexe=f
tel=07777777777
etage=1/2
dpt=bloc <12>

[13]
nom=mon_nom2
prenom=mon_prenom2
code=3333
adresse=mon_adresse1
postal=10752
sexe=f
tel=066666666
etage=2/2
dpt=bloc <13>

'@ > $File
[/code:1]
Le code d'analyse des lignes d'un enregistrement (ici 11 par bloc) basé sur des regex :
[code:1]
$sbSplit={
#Ne déclare pas de paramètre mais référence l'objet du pipeline
# Une regex peut construire un membre de type NoteProperty ou d'un autre type
#La fonction Convert-BlockStructuredFile construit en interne l'objet et
#lui ajoute chaque nouveau membre créé par ce scriptblock

switch -regex ($_)
{
#séparateur + valeur sur quatre chiffres + séparateur
'^\[(?<Number>\d{1,4})\]$' {
Write-debug (\"`tAjoute la propriété {0}\"-F $Matches.0)
New-Object Management.Automation.PSNoteProperty('Number',$Matches.Number)
Continue
}

#Nom + séparateur + valeur
'^(?<Name>.*?)=(?<Value>.*)$' {
Write-debug (\"`tAjoute la propriété {0}\"-F $Matches.0)
New-Object Management.Automation.PSNoteProperty($Matches.Name, $Matches.Value)
Continue
}

'' { Write-Debug \"`tLigne vide\";Continue }
default {Write-Error 'Cas inconnu : $Element' }
}
} #$sbSplit
[/code:1]
Liste des propriétés pour contrôler la création de l'enregistrement :
[code:1]
$properties=@(
'nom',
'prenom',
'code',
'adresse',
'postal',
'sexe',
'tel',
'etage',
'dpt'
)
[/code:1]
Enfin l'appel de la fonction une fois les prérequis construit :
[code:1]
$Objets= Get-Content $File|Convert-BlockStructuredFile -LinePerBloc 11 -Property $properties -Split $sbSplit
$Objets
# Number : 10
# nom : mon_nom
# prenom : mon_prenom
# code : 1111
# adresse : mon_adresse
# postal : 12345
# sexe : m
# tel : 1234567890
# etage : 1/1
# dpt : bloc <10>
#
# Number : 12
# nom : mon_nom1
#...
[/code:1]
On peut aussi déclarer un filtre au lieu d'un scriptblock et l'utiliser avec la fonction sous réserve de respecter la syntaxe :
[code:1]
filter SplitBlock {
switch -regex ($_)
{
#séparateur + valeur sur quatre chiffres + séparateur
'^\[(?<Number>\d{1,4})\]$' {
Write-debug (\"`tAjoute la propriété {0}\"-F $Matches.0)
New-Object Management.Automation.PSNoteProperty('Number',$Matches.Number)
Continue
}

#Nom + séparateur + valeur
'^(?<Name>.*?)=(?<Value>.*)$' {
Write-debug (\"`tAjoute la propriété {0}\"-F $Matches.0)
New-Object Management.Automation.PSNoteProperty($Matches.Name, $Matches.Value)
Continue
}

'' { Write-Debug \"`tLigne vide\";Continue }
default {Write-Error 'Cas inconnu : $Element' }
}
}#SplitBlock

$Objets= Get-Content $File|Convert-BlockStructuredFile -LinePerBloc 11 -Property $properties -Split ${function:«»SplitBlock}
[/code:1]
[edit]
Autre approche <br><br>Message édité par: Laurent Dardenne, à: 14/06/18 18:07

Tutoriels PowerShell

Connexion ou Créer un compte pour participer à la conversation.

Temps de génération de la page : 0.203 secondes
Propulsé par Kunena