[..] Ordonner un tableau / une listview contenant des adresses IP

Aide et conseils concernant AutoIt et ses outils.
Règles du forum
.
Répondre
jpascal
Niveau 6
Niveau 6
Messages : 239
Enregistré le : jeu. 16 oct. 2008 16:21
Status : Hors ligne

[..] Ordonner un tableau / une listview contenant des adresses IP

#1

Message par jpascal »

Bonjour,

Je souhaite pouvoir ordonner un tableau contenant des adresses IP.

Je suis en train de regarder différents UDF (ArrayMultiSortCase, arrayplus, ...) sans réellement trouver mon bonheur.

J'ai codé mon propre algo mais je souhaiterais savoir si une solution existe déjà. :idea:
#include <Array.au3>

Main()

Func Main()
   Local $aIP = [['Nom', 'IP'], ['riri', '192.168.30.4'], ['fifi', '192.168.120.10'], ['loulou', '10.5.250.5'], ['toto', '192.168.4.45']]

   ; Test 2D
   Local $a2D = $aIP
   _ArrayDisplay($a2D, 'initial')
   SortIP($a2D, 0, 1, 0, 1)
   _ArrayDisplay($a2D, 'ordered')

   ; Test 1D
   Local $a1D = $aIP
   _ArrayColDelete($a1D, 0, True) ; suppression colonne 0
   _ArrayDelete($a1D, 0) ; suppression ligne 0
   _ArrayDisplay($a1D, 'initial')
   SortIP($a1D)
   _ArrayDisplay($a1D, 'ordered')
EndFunc   ;==>Main

Func SortIP(ByRef $aArray, $iDescending = 0, $iStart = 0, $iEnd = 0, $iSubItem = 0)
   Local $aRegExp[0]
   Local $iColOrder = 1

   If $iEnd = 0 Then $iEnd = UBound($aArray) - 1
   Switch UBound($aArray, $UBOUND_DIMENSIONS)
      Case 1 ; 1D array
         _ArrayColInsert($aArray, 1)
         For $iItem = $iStart To $iEnd
            $aRegExp = StringRegExp($aArray[$iItem][$iSubItem], '(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})', $STR_REGEXPARRAYGLOBALMATCH)
            If @error = 0 Then $aArray[$iItem][$iColOrder] = $aRegExp[0] * 1000000000 + $aRegExp[1] * 1000000 + $aRegExp[2] * 1000 + $aRegExp[3]
         Next

      Case 2 ; 2D array
         $iColOrder = UBound($aArray, $UBOUND_COLUMNS)
         ReDim $aArray[$iEnd + 1][$iColOrder + 1]
         For $iItem = $iStart To $iEnd
            $aRegExp = StringRegExp($aArray[$iItem][$iSubItem], '(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})', $STR_REGEXPARRAYGLOBALMATCH)
            If @error = 0 Then $aArray[$iItem][$iColOrder] = $aRegExp[0] * 1000000000 + $aRegExp[1] * 1000000 + $aRegExp[2] * 1000 + $aRegExp[3]
         Next
   EndSwitch

   _ArraySort($aArray, $iDescending, $iStart, $iEnd, $iColOrder)
   _ArrayColDelete($aArray, $iColOrder)
EndFunc   ;==>SortIP

Question subsidiaire : je souhaite effectuer la même opération sur les données d'une ListView.
Il me semblait avoir vu passer une fonction qui permet d'ordonner en renvoyant chaque donnée vers une sous-fonction (Callback). Mais je ne la retrouve plus. :(
L'UDF _GUIListViewEx propose la fonction _GUIListViewEx_ReadToArray() mais je préfèrerais éviter d'utiliser cette usine à gaz pour ce simple besoin.
Modifié en dernier par jpascal le mer. 28 août 2024 14:51, modifié 1 fois.
AutoIt 3.3.16.1 - AutoIt3Wrapper 21.316.1639.1
Avatar du membre
sylvanie
Niveau 11
Niveau 11
Messages : 1552
Enregistré le : jeu. 26 juil. 2007 21:31
Localisation : Paris
Status : Hors ligne

Re: [..] Ordonner un tableau contenant des adresses IP

#2

Message par sylvanie »

Bonsoir
Personnellement, je convertirai les IP en entiers non signés par des opérations de décalages et de masques.
En effet une IP (en Ipv4) est l'expression de 4 octets non signés, donc on peut convertir par exemple 192.168.10.1 en 192*256^3+168*256^2+10*256+1 (ce qui est fait par exemple dans pas mal de bibliothèque en C)
A partir de là, on compare des entiers.

Pour la callback de tri, j'ai adapté l'exemple fourni dans l'aide concernant effectivement GUICtrlRegisterListViewSort

Code : Tout sélectionner

; Test conversion IP

#include <Array.au3>

Func IP_to_Int($IP)
	Local $tab_ip=StringSplit($IP,".")
	Local $Returnval=0
	If @error<>0 Then
		SetError(1)
		Return(0)
	EndIf
	If $tab_ip[0] <> 4 Then
		SetError(2)
		Return(0)
	EndIf
	For $indice = 1 to 4
		$Returnval+=$tab_ip[$indice]*BitShift(1,-8*(4-$indice))
	Next
	SetError(0)
	Return $Returnval
EndFunc

Func Int_to_IP($VALIP)
	Local $Returnval=""
	Local $Ctrl
	$Ctrl=StringRegExp($VALIP,"^[0-9]+$")
	If $Ctrl<>1 Then
		SetError(1)
		Return("")
	EndIf
	If $VALIP > 4294967295 Then
		SetError(2)
		Return("")
	EndIf
	For $indice = 1 to 4
		Local $Current_Byte=""
		$Current_Byte=BitAND($VALIP,255)
		$Returnval="."&$Current_Byte&$Returnval
		$VALIP=BitShift($VALIP,8)
	Next
	SetError(0)
	$Returnval=StringReplace($Returnval,".","",1)
	Return $Returnval
EndFunc


; Test de tri via tableau 2D
Local $aIP = [['riri', '192.168.30.4'], ['fifi', '192.168.120.10'], ['loulou', '10.5.250.5'], ['toto', '192.168.4.45']]
For $indice = 0 To UBound($aIP,$UBOUND_ROWS)-1
	$aIP[$indice][1]=IP_to_Int($aIP[$indice][1])
Next

_ArraySort($aIP,0,0,0,1)
For $indice = 0 To UBound($aIP,$UBOUND_ROWS)-1
	$aIP[$indice][1]=Int_to_IP($aIP[$indice][1])
Next
_ArrayDisplay($aIP)

; sorting 3 column's different

#include <GUIConstantsEx.au3>
#include <ListViewConstants.au3>

Global $g_iCurCol = -1
Global $g_iSortDir = 1
Global $g_bSet = False
Global $g_iCol = -1

Example()

Func Example()
    GUICreate("Test", 300, 200)

    Local $idLv = GUICtrlCreateListView("Column1|Col2", 10, 10, 280, 180)
    GUICtrlRegisterListViewSort(-1, "LVSort") ; Register the function "LVSort" for the sorting callback
	For $indice= 0 To UBound($aIP,$UBOUND_ROWS)-1
		GUICtrlCreateListViewItem($aIP[$indice][0]&"|"&$aIP[$indice][1], $idLv)
	Next
    GUISetState(@SW_SHOW)

    Local $idMsg
    ; Loop until the user exits.
    While 1
        $idMsg = GUIGetMsg()
        Switch $idMsg
            Case $GUI_EVENT_CLOSE
                ExitLoop

            Case $idLv
                $g_bSet = False
                $g_iCurCol = $g_iCol
                GUICtrlSendMsg($idLv, $LVM_SETSELECTEDCOLUMN, GUICtrlGetState($idLv), 0)
        EndSwitch
    WEnd

    GUIDelete()
EndFunc   ;==>Example

; Our sorting callback funtion
Func LVSort($hWnd, $nItem1, $nItem2, $iColumn)
    Local $sVal1, $sVal2, $iResult

    ; Switch the sorting direction
    If $iColumn = $g_iCurCol Then
        If Not $g_bSet Then
            $g_iSortDir = $g_iSortDir * -1
            $g_bSet = True
        EndIf
    Else
        $g_iSortDir = 1
    EndIf
    $g_iCol = $iColumn

    $sVal1 = GetSubItemText($hWnd, $nItem1, $iColumn)
    $sVal2 = GetSubItemText($hWnd, $nItem2, $iColumn)

    If $iColumn = 1 Then
        $sVal1 = IP_to_Int($sVal1)
        $sVal2 = IP_to_Int($sVal2)
    EndIf

    $iResult = 0 ; No change of item1 and item2 positions

    If $sVal1 < $sVal2 Then
        $iResult = -1 ; Put item2 before item1
    ElseIf $sVal1 > $sVal2 Then
        $iResult = 1 ; Put item2 behind item1
    EndIf

    $iResult = $iResult * $g_iSortDir

    Return $iResult
EndFunc   ;==>LVSort

; Retrieve the text of a listview item in a specified column
Func GetSubItemText($idCtrl, $idItem, $iColumn)
    Local $tLvfi = DllStructCreate("uint;ptr;int;int[2];int")

    DllStructSetData($tLvfi, 1, $LVFI_PARAM)
    DllStructSetData($tLvfi, 3, $idItem)

    Local $tBuffer = DllStructCreate("char[260]")

    Local $nIndex = GUICtrlSendMsg($idCtrl, $LVM_FINDITEM, -1, DllStructGetPtr($tLvfi));

    Local $tLvi = DllStructCreate("uint;int;int;uint;uint;ptr;int;int;int;int")

    DllStructSetData($tLvi, 1, $LVIF_TEXT)
    DllStructSetData($tLvi, 2, $nIndex)
    DllStructSetData($tLvi, 3, $iColumn)
    DllStructSetData($tLvi, 6, DllStructGetPtr($tBuffer))
    DllStructSetData($tLvi, 7, 260)

    GUICtrlSendMsg($idCtrl, $LVM_GETITEMA, 0, DllStructGetPtr($tLvi));

    Local $sItemText = DllStructGetData($tBuffer, 1)

    Return $sItemText
EndFunc   ;==>GetSubItemText



;$IP="192.168.1.10"
;$INT=IP_to_Int($IP)
;ConsoleWrite("Int="&$INT&@CRLF)
;ConsoleWrite("IP="&Int_to_IP($INT)&@crlf)

En espérant que ceci vous aide (aux bugs prêts ^^')
Toi qui cherche à mettre le doigt sur la solution, appuie sur F1.
jpascal
Niveau 6
Niveau 6
Messages : 239
Enregistré le : jeu. 16 oct. 2008 16:21
Status : Hors ligne

Re: [..] Ordonner un tableau contenant des adresses IP

#3

Message par jpascal »

Bonjour sylvanie,

Un grand merci pour votre solution et votre temps. :-)

En plus de la solution, je vois comment il est possible de se passer de la fonction _GUICtrlListView_GetItemText() pour récupérer un "subitem".

Petites questions :

Plutôt que d'utiliser :
   $Ctrl = StringRegExp($VALIP, "^[0-9]+$")
   If $Ctrl <> 1 Then
      SetError(1)
      Return ("")
   EndIf
puis-je écrire :
If Not StringRegExp($VALIP, "^[0-9]+$") Then Return SetError(1, 0, '')


A quoi sert la ligne SetError(0) à la fin des fonctions IP_to_Int() et Int_to_IP() ?
AutoIt 3.3.16.1 - AutoIt3Wrapper 21.316.1639.1
Avatar du membre
jl56
Niveau 7
Niveau 7
Messages : 415
Enregistré le : mer. 24 oct. 2007 22:42
Localisation : 56000
Status : Hors ligne

Re: [..] Ordonner un tableau contenant des adresses IP

#4

Message par jl56 »

Bonjour,

J'avais fait cette demande il y longtemps sur ce forum et Mikell m'avais donné cette solution que j'utilise toujours

viewtopic.php?f=3&t=13334&p=93273#p93273

Cordialement,

Jean-Luc
Avatar du membre
sylvanie
Niveau 11
Niveau 11
Messages : 1552
Enregistré le : jeu. 26 juil. 2007 21:31
Localisation : Paris
Status : Hors ligne

Re: [..] Ordonner un tableau contenant des adresses IP

#5

Message par sylvanie »

Bonjour,
Effectivement pour le contrôle de la validité il y a sûrement plus court, j' ai couché mes lignes vite fait ^^
Quant au seterror(0) , je ne savais plus si par défaut c'était fait, ce qui en tout bon sens doit être le cas. Au moins je suis sûr que c'est fait 😉
Toi qui cherche à mettre le doigt sur la solution, appuie sur F1.
jpascal
Niveau 6
Niveau 6
Messages : 239
Enregistré le : jeu. 16 oct. 2008 16:21
Status : Hors ligne

Re: [..] Ordonner un tableau contenant des adresses IP

#6

Message par jpascal »

Bonjour jl56,

Merci pour le lien. :-)
Utiliser la fonction StringFormat() est une idée plus simple que la mienne.

Souhaitant pouvoir utiliser l'indicateur de direction du tri, j'ai fait le choix d'utiliser l'UDF GuiListView

J'ai créé mon propre UDF pour gagner du temps. Voici le code avec l'exemple intégré :
; === UDF Part 1 ===

#include <Array.au3>
#include <GUIConstantsEx.au3>
#include <GuiListView.au3>
#include <ListViewConstants.au3>

Global $miColIP[] ; permet de prendre en charge plusieurs ListView avec différents numéros de colonne pour les adresses IP


; === Example ===

Global $g_aIP = [['riri', '192.168.30.4'], ['fifi', '192.168.120.10'], ['loulou', '10.5.250.5'], ['toto', '192.168.4.45']]

Example()

Func Example()
   GUICreate('Test', 300, 200)

   Local $idListView = GUICtrlCreateListView('Col1|Col2', 10, 10, 280, 180)
   _GUICtrlListView_RegisterSortCallBack($idListView, 0, True, '__GUICtrlListView_SortIP') ; Register the function '__GUICtrlListView_SortIP' for the sorting callback
   For $i = 0 To UBound($g_aIP, $UBOUND_ROWS) - 1
      GUICtrlCreateListViewItem($g_aIP[$i][0] & '|' & $g_aIP[$i][1], $idListView)
   Next
   GUISetState(@SW_SHOW)

   $miColIP[GUICtrlGetHandle($idListView)] = 1 ; numéro de colonne contenant les adresses IP de la ListView

   Local $idMsg
   ; Loop until the user exits.
   While 1
      $idMsg = GUIGetMsg()
      Switch $idMsg
         Case $GUI_EVENT_CLOSE
            ExitLoop

         Case $idListView
            _GUICtrlListView_SortItems($idListView, GUICtrlGetState($idListView))
      EndSwitch
   WEnd

   _GUICtrlListView_UnRegisterSortCallBack($idListView)
   GUIDelete()
EndFunc   ;==>Example

; === UDF Part 2 ===

Func _ArraySortIP(ByRef $aArray, $iDescending = 0, $iStart = 0, $iEnd = 0, $iSubItem = 0)
   For $i = 0 To UBound($aArray, $UBOUND_ROWS) - 1
      $aArray[$i][1] = __IPtoINT($aArray[$i][1])
   Next
   _ArraySort($aArray, $iDescending, $iStart, $iEnd, $iSubItem)
   For $i = 0 To UBound($aArray, $UBOUND_ROWS) - 1
      $aArray[$i][1] = __INTtoIP($aArray[$i][1])
   Next
EndFunc   ;==>_ArraySortIP

Func __IPtoINT($sIP)
   Local $aIP = StringSplit($sIP, '.')
   Local $Return = 0

   If @error <> 0 Then Return SetError(1, 0, 0)
   If $aIP[0] <> 4 Then Return SetError(2, 0, 0)
   For $i = 1 To 4
      $Return += $aIP[$i] * BitShift(1, -8 * (4 - $i))
   Next

   Return $Return
EndFunc   ;==>__IPtoINT

Func __INTtoIP($iIP)
   Local $iCurrentByte = 0
   Local $Return = ''

   If Not StringRegExp($iIP, '^[0-9]+$') Then Return SetError(1, 0, '')
   If $iIP > 4294967295 Then Return SetError(2, 0, '')
   For $i = 1 To 4
      $iCurrentByte = BitAND($iIP, 255)
      $Return = '.' & $iCurrentByte & $Return
      $iIP = BitShift($iIP, 8)
   Next
   $Return = StringReplace($Return, '.', '', 1)

   Return $Return
EndFunc   ;==>__INTtoIP

#Au3Stripper_Ignore_Funcs=__GUICtrlListView_SortIP
Func __GUICtrlListView_SortIP($nItem1, $nItem2, $hWnd)
   Local $iIndex, $sVal1, $sVal2, $nResult
   Local $tBuffer, $iMsg

   If $__g_aListViewSortInfo[$iIndex][0] Then
      $tBuffer = $__g_tListViewBuffer
      $iMsg = $LVM_GETITEMTEXTW
   Else
      $tBuffer = $__g_tListViewBufferANSI
      $iMsg = $LVM_GETITEMTEXTA
   EndIf
   Local $tItem = $__g_tListViewItem

   For $x = 1 To $__g_aListViewSortInfo[0][0]
      If $hWnd = $__g_aListViewSortInfo[$x][1] Then
         $iIndex = $x
         ExitLoop
      EndIf
   Next

   ; Switch the sorting direction
   If $__g_aListViewSortInfo[$iIndex][3] = $__g_aListViewSortInfo[$iIndex][4] Then ; $nColumn = nCurCol ?
      If Not $__g_aListViewSortInfo[$iIndex][7] Then ; $bSet
         $__g_aListViewSortInfo[$iIndex][5] *= -1 ; $nSortDir
         $__g_aListViewSortInfo[$iIndex][7] = 1 ; $bSet
      EndIf
   Else
      $__g_aListViewSortInfo[$iIndex][7] = 1 ; $bSet
   EndIf
   $__g_aListViewSortInfo[$iIndex][6] = $__g_aListViewSortInfo[$iIndex][3] ; $nCol = $nColumn
   DllStructSetData($tItem, 'Mask', $LVIF_TEXT)
   DllStructSetData($tItem, 'SubItem', $__g_aListViewSortInfo[$iIndex][3])

   __GUICtrl_SendMsg($hWnd, $iMsg, $nItem1, $tItem, $tBuffer, False, 6, True)
   $sVal1 = DllStructGetData($tBuffer, 1)
   __GUICtrl_SendMsg($hWnd, $iMsg, $nItem2, $tItem, $tBuffer, False, 6, True)
   $sVal2 = DllStructGetData($tBuffer, 1)

   If $__g_aListViewSortInfo[$iIndex][3] = $miColIP[$hWnd] Then
      $sVal1 = __IPtoINT($sVal1)
      $sVal2 = __IPtoINT($sVal2)
   EndIf

   ; $vCompareType / $__g_aListViewSortInfo[$iIndex][8] is NOT used

   $nResult = 0 ; No change of item1 and item2 positions
   If $sVal1 < $sVal2 Then
      $nResult = -1 ; Put item2 before item1
   ElseIf $sVal1 > $sVal2 Then
      $nResult = 1 ; Put item2 behind item1
   EndIf
   $nResult = $nResult * $__g_aListViewSortInfo[$iIndex][5] ; $nSortDir

   Return $nResult
EndFunc   ;==>__GUICtrlListView_SortIP

A noter la présence de la fonction _ArraySortIP() pour pouvoir trier simplement un tableau.
AutoIt 3.3.16.1 - AutoIt3Wrapper 21.316.1639.1
jpascal
Niveau 6
Niveau 6
Messages : 239
Enregistré le : jeu. 16 oct. 2008 16:21
Status : Hors ligne

Re: [..] Ordonner un tableau / une listview contenant des adresses IP

#7

Message par jpascal »

A noter également que je ne prends pas en charge le cas où plusieurs colonnes contiennent des adresses IP.
AutoIt 3.3.16.1 - AutoIt3Wrapper 21.316.1639.1
Répondre