Question [Fonction] Convert-BlockStructuredFile

Plus d'informations
il y a 5 ans 11 mois - il y a 2 semaines 1 jour #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.
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 
Un fichier d'exemple :
$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
Le code d'analyse des lignes d'un enregistrement (ici 11 par bloc) basé sur des regex :
$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
Liste des propriétés pour contrôler la création de l'enregistrement :
$properties=@(
'nom',
'prenom',
'code',
'adresse',
'postal',
'sexe',
'tel',
'etage',
'dpt'
)
Enfin l'appel de la fonction une fois les prérequis construit :
$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 
#...
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 :
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} 
[edit]
Autre approche <br><br>Message édité par: Laurent Dardenne, à: 14/06/18 18:07

Tutoriels PowerShell
Dernière édition: il y a 2 semaines 1 jour par Laurent Dardenne.

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

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