Chimaera

Remove from array with wildcard?

27 posts in this topic

I have a program that logs uninstall keys for programs and adds them to a csv

It has an exemption array of programs i don't want include in the list, but its getting a bit out of hand.

Is there a way to use wildcards to remove things from an array?

example of the remove array

$sRemoveArray &= '|Update for 2007 Microsoft Office System (KB967642)|Update for Microsoft Office 2007 Help for Common Features (KB963673)|Update for Microsoft Office 2007 suites (KB2596620) 32-Bit Edition|Update for Microsoft Office 2007 suites (KB2596787) 32-Bit Edition|Update for Microsoft Office 2007 suites (KB2767849) 32-Bit Edition'

If i could use

$sRemoveArray &= '|Update for 2007 Micr*'

I could remove 3 bloody great big array just on that one alone

I remove the items like this

For $removeloop = 1 To UBound($aRemoveArray) - 1
    $search = _ArraySearch($aList, $aRemoveArray[$removeloop]) ; Look for the index of the item
;~  ConsoleWrite($search & ' - ' & @error & ' - ' & $aRemoveArray[$removeloop] & @CRLF)
    If $search <> -1 Then ; If found...
        _ArrayDelete($aList, $search) ; ...then remove it
    EndIf
Next

I looked at this because i thought it would maybe do it, but im struggling to make sense of it in relation to deleting array lines

#include <Array.au3>

Local $aTest_Array[3] = ['Testing`_Temp.jpg', 'JOHN.jpeg', 'Boshe.jpeg']
Local $Ret_Array
$Ret_Array = _ArrayFindAllEx($aTest_Array, '*_Temp.jp?g|*h?.jp?g', True, 'Test*.jp?g|b*.jpeg', False)
_ArrayDisplay($Ret_Array, @extended & ' Match')

#cs
Wildcards
*  - Zero or more character
+  - One or more character
?  - No or one character
#ce
; #FUNCTION# ====================================================================================================================
; Name ..........: _ArrayFindAllEx
; Description ...: Similar to _ArrayFindAll with Include, Exclude masks
; Syntax ........: _ArrayFindAllEx(Const Byref $aArray, $sIncludeMask[, $fIncSenstive = True[, $sExcludeMask = ''[,
;                  $fExcSenstive = True]]])
; Parameters ....: $aArray              - [in/out and const] The Array to search for the values.
;                  $sIncludeMask        - A string value.
;                  $fIncSenstive        - [optional] A boolean value. Default is True.
;                  $sExcludeMask        - [optional] A string value. Default is ''.
;                  $fExcSenstive        - [optional] A boolean value. Default is True.
; Return values .: Sucess - Returns the array of the Index of the found values
; - @extended is set to the number of items found
;    Failure  -  Returns '' and sets @error to 1
; Author ........: Phoenix XL
; Modified ......:
; Remarks .......:
; Related .......: _ArrayFindAll
; Link ..........:
; Example .......: Yes
; ===============================================================================================================================
Func _ArrayFindAllEx(ByRef Const $aArray, $sIncludeMask, $fIncSenstive = True, $sExcludeMask = '', $fExcSenstive = True)
;Set Sensitivity Values
If $fIncSenstive Then
$fIncSenstive = ''
Else
$fIncSenstive = '(?i)'
EndIf
If $fExcSenstive Then
$fExcSenstive = ''
Else
$fExcSenstive = '(?i)'
EndIf

;Make the Include Mask Pattern
$sIncludeMask = StringRegExpReplace($sIncludeMask, '(?s)([^\w|])', '[\1]')
$sIncludeMask = StringRegExpReplace($sIncludeMask, '(?s)(\[\?\])', '.?')
$sIncludeMask = StringRegExpReplace($sIncludeMask, '(?s)(\[\+\])', '.+')
$sIncludeMask = $fIncSenstive & '(?s)\A(' & StringRegExpReplace($sIncludeMask, '(?s)(\[\*\])', '.*') & ')\z'

Local $aRet[1]
;Debug Out Include Mask Patterns
;~ ConsoleWrite($sIncludeMask & @CR)

;Get the to-include Strings
For $i = 0 To UBound($aArray) - 1
If StringRegExp($aArray[$i], $sIncludeMask) Then _ArrayAdd($aRet, $i)
Next
_ArrayDelete($aRet, 0)
If Not IsArray($aRet) Then Return 0
If Not $sExcludeMask Then Return SetExtended( UBound( $aRet ), $aRet )

;Make the Exclude Mask Pattern
$sExcludeMask = StringRegExpReplace($sExcludeMask, '(?s)([^\w|])', '[\1]')
$sExcludeMask = StringRegExpReplace($sExcludeMask, '(?s)(\[\?\])', '.?')
$sExcludeMask = StringRegExpReplace($sExcludeMask, '(?s)(\[\+\])', '.+')
$sExcludeMask = $fExcSenstive & '(?s)\A(' & StringRegExpReplace($sExcludeMask, '(?s)(\[\*\])', '.*') & ')\z'

;Debug Out Exclude Mask Patterns
;~ ConsoleWrite($sExcludeMask & @CR)
Local $nDeleted = 0
;Delete the to-exclude strings
For $i = 0 To UBound($aRet) - 1
If StringRegExp($aArray[$aRet[$i - $nDeleted]], $sExcludeMask) Then $nDeleted += Number(_ArrayDelete($aRet, $i - $nDeleted) > 0)
Next
;Done
Return SetError(Not IsArray($aRet), UBound($aRet), $aRet)

EndFunc   ;==>_ArrayFindAllEx

 

Share this post


Link to post
Share on other sites



May I suggest the same as JO but using

For $removeloop = UBound($aList)-1 To 1 step -1

:)

1 person likes this

Share this post


Link to post
Share on other sites

This could work too :

#Include <Array.au3>

$sExclude = "val*remove*|val*delete ?"

Local $aArray = ["value to keep 1", "value to keep 2", "value to keep 3", "value to remove 1", "value to remove 2", "value to keep 4", "value to delete 1"]

$sExclude = StringReplace($sExclude, ".", "\.")
$sExclude = StringReplace($sExclude, "?", ".")
$sExclude = StringReplace($sExclude, "*", ".*?")

$iIndex = 0
For $i = 0 To UBound($aArray) - 1
    If NOT StringRegExp($aArray[$i], $sExclude) Then
        $aArray[$iIndex] = $aArray[$i]
        $iIndex += 1
    EndIf   
Next
Redim $aArray[$iIndex]

_ArrayDisplay($aArray)

 

1 person likes this

Share this post


Link to post
Share on other sites

Thx guys 

I was so focused on the wildcard i forgot to think of a simpler solution

May I suggest the same as JO but using

For $removeloop = UBound($aList)-1 To 1 step -1

What benefit does the extra steps have?

Share this post


Link to post
Share on other sites

When using _ArrayDelete in a For loop it's necessary to loop backwards  :)

Share this post


Link to post
Share on other sites

Once you delete an element from an array, all the higher elements acquire a new index number. So each subsequent element will be skipped. Lower indices are not affected when you delete an array element, so looping backwards prevents this problem.

Share this post


Link to post
Share on other sites

#9 ·  Posted (edited)

Good point jguinch! _ArrayDelete will redim the array many times instead of just once, as you have in your code. It's a better approach.

Edited by czardas

Share this post


Link to post
Share on other sites

#10 ·  Posted (edited)

Provided you dont have a "k" word indicating removal (like kill), you could also remove anything that is not "keep" by removing anything that doesnt start with a k right there.  The expression could probably stand to be refined.  **and then stringsplit that on the $sep to get your array back, naturally.

$sep = ":"
$sExclude = "value to [^k].*?\d+" & $sep & "*"

Local $aArray = ["value to keep 1", "value to delete 1", "value to keep 2", "value to remove 1", "value to remove 2", "value to keep 3"]

msgbox(0, '' , StringRegExpReplace(_ArrayToString($aArray , $sep) , $sExclude , ""))

 

Edited by boththose

,-. .--. ________ .-. .-. ,---. ,-. .-. .-. .-.
|(| / /\ \ |\ /| |__ __||| | | || .-' | |/ / \ \_/ )/
(_) / /__\ \ |(\ / | )| | | `-' | | `-. | | / __ \ (_)
| | | __ | (_)\/ | (_) | | .-. | | .-' | | \ |__| ) (
| | | | |)| | \ / | | | | | |)| | `--. | |) \ | |
`-' |_| (_) | |\/| | `-' /( (_)/( __.' |((_)-' /(_|
'-' '-' (__) (__) (_) (__)

Share this post


Link to post
Share on other sites

#11 ·  Posted (edited)

Ive tried the first suggestions but still not got it working yet it cant seem to see the array but the arraydisplay right before can see it perfectly

so i moved onto the next

$sExclude = StringReplace($sExclude, ".", "\.")
$sExclude = StringReplace($sExclude, "?", ".")
$sExclude = StringReplace($sExclude, "*", ".*?")

what does this bit do exactly?

Edited by Chimaera

Share this post


Link to post
Share on other sites

It changes an expression containing the usual wildcards

$sExclude = "val*remove*|val*delete ?"

to a pattern to be used in a regex

$sExclude = "val.*?remove.*?|val.*?delete ."

Let' say, it's a matter of style  :)

1 person likes this

Share this post


Link to post
Share on other sites

#13 ·  Posted (edited)

Ok i think ive worked out why neither of these work, i think my array is 2D but im not sure

heres a test code if someone can check plz

I get this error no matter which example i use

"C:\Users\***\Desktop\test.au3" (19) : ==> Array variable has incorrect number of subscripts or subscript dimension range exceeded.:
If NOT StringRegExp($aList[$i], $sExclude) Then
If NOT StringRegExp(^ ERROR
#Include <Array.au3>
#include <Date.au3>

$aList = _UninstallList("UninstallString", ".+", "UninstallString", 3, 3) ; |QuietUninstallString
_ArrayDisplay($aList, "UninstallString", Default, Default, Default, "DisplayName|Date|RegistryPath|RegistrySubKey|UninstallString|QuietUninstallString")

$sExclude = "Microsoft V*|Security U*"

;~ Local $aArray = ["Microsoft Visual C++ 2005 Redistributable", "Microsoft Visual C++ 2008 Redistributable - x64 9.0.30729.17", "Microshaft Visual", "Security Update for Microsoft .NET Framework 4 Client Profile (KB2604121)", "MSI Live Update", "Microsoft Lync 2010", "Microsoft_VC90_CRT_x86"]

$sExclude = StringReplace($sExclude, ".", "\.")
$sExclude = StringReplace($sExclude, "?", ".")
$sExclude = StringReplace($sExclude, "*", ".*?")

$iIndex = 0
For $i = 0 To UBound($aList) - 1
    If NOT StringRegExp($aList[$i], $sExclude) Then
        $aList[$iIndex] = $aList[$i]
        $iIndex += 1
    EndIf
Next
Redim $aList[$iIndex]

_ArrayDisplay($aList, "UninstallString", Default, Default, Default, "DisplayName|Date|RegistryPath|RegistrySubKey|UninstallString|QuietUninstallString")
;~ _ArrayDisplay($aList)

; #FUNCTION# ====================================================================================================================
; Name ..........: _UninstallList
; Description ...: Returns an array of matching uninstall keys from registry, with an optional filter
; Syntax ........: _UninstallList([$sValueName = ""[, $sFilter = ""[, $sCols = ""[, $iSearchMode = 0[,$ iArch = 3]]]]]])
; Parameters ....: $sValueName       - [optional] Registry value used for the filter.
;                                          Default is all keys ($sFilter do not operates).
;                  $sFilter          - [optional] String to search in $sValueName. Filter is not case sensitive.
;                  $sCols            - [optional] Additional values to retrieve. Use "|" to separate each value.
;                                          Each value adds a column in the returned array
;                  $iSearchMode      - [optional] Search mode. Default is 0.
;                                          0 : Match string from the start.
;                                          1 : Match any substring.
;                                          2 : Exact string match.
;                                          3 : $sFilter is a regular expression
;                  $iArch            - [optional] Registry keys to search in. Default is 3.
;                                          1 : x86 registry keys only
;                                          2 : x64 registry keys only
;                                          3 : both x86 and x64 registry keys
; Return values .: Returns a 2D array of registry keys and values :
;                      $array[0][0] : Number of keys
;                      $array[n][0] : Registry key path
;                      $array[n][1] : Registry subkey
;                      $array[n][2] : Display name
;                      $array[n][3] : Installation date (YYYYMMDD format)
;                      $array[n][4] : 1st additional value specified in $sCols (only if $sCols is set)
;                      $array[n][5] : 2nd additional value specified in $sCols (only if $sCols contains at least 2 entries)
;                      $array[n][x] : Nth additional value ...
; Author ........: jguinch
; ===============================================================================================================================
Func _UninstallList($sValueName = "", $sFilter = "", $sCols = "", $iSearchMode = 0, $iArch = 3)
    Local $sHKLMx86, $sHKLM64, $sHKCU = "HKCU\Software\Microsoft\Windows\CurrentVersion\Uninstall"
    Local $aKeys[1] = [$sHKCU]
    Local $sDisplayName, $sSubKey, $sKeyDate, $sDate, $sValue, $iFound, $n, $aResult[1][4], $iCol
    Local $aCols[1] = [0]

    If Not IsInt($iArch) Or $iArch < 0 Or $iArch > 3 Then Return SetError(1, 0, 0)
    If Not IsInt($iSearchMode) Or $iSearchMode < 0 Or $iSearchMode > 3 Then Return SetError(1, 0, 0)

    $sCols = StringRegExpReplace(StringRegExpReplace($sCols, "(?i)(DisplayName|InstallDate)\|?", ""), "\|$", "")
    If $sCols <> "" Then $aCols = StringSplit($sCols, "|")

    If @OSArch = "X86" Then
        $iArch = 1
        $sHKLMx86 = "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
    Else
        If @AutoItX64 Then
            $sHKLMx86 = "HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
            $sHKLM64 = "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
        Else
            $sHKLMx86 = "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
            $sHKLM64 = "HKLM64\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
        EndIf
    EndIf

    If BitAND($iArch, 1) Then
        ReDim $aKeys[UBound($aKeys) + 1]
        $aKeys[UBound($aKeys) - 1] = $sHKLMx86
    EndIf

    If BitAND($iArch, 2) Then
        ReDim $aKeys[UBound($aKeys) + 1]
        $aKeys[UBound($aKeys) - 1] = $sHKLM64
    EndIf


    For $i = 0 To UBound($aKeys) - 1
        $n = 1
        While 1
            $iFound = 1
            Local $aSubKey = _RegEnumKeyEx($aKeys[$i], $n)
            If @error Then ExitLoop

            $sSubKey = $aSubKey[0]
            $sKeyDate = StringRegExpReplace($aSubKey[1], "^(\d{4})/(\d{2})/(\d{2}).+", "$1$2$3")
            $sDisplayName = RegRead($aKeys[$i] & "\" & $sSubKey, "DisplayName")
            $sDate = RegRead($aKeys[$i] & "\" & $sSubKey, "InstallDate")
            If $sDate = "" Then $sDate = $sKeyDate

            If $sDisplayName <> "" Then
                If $sValueName <> "" Then
                    $iFound = 0
                    $sValue = RegRead($aKeys[$i] & "\" & $sSubKey, $sValueName)
                    If ($iSearchMode = 0 And StringInStr($sValue, $sFilter) = 1) Or _
                            ($iSearchMode = 1 And StringInStr($sValue, $sFilter)) Or _
                            ($iSearchMode = 2 And $sValue = $sFilter) Or _
                            ($iSearchMode = 3 And StringRegExp($sValue, $sFilter)) Then
                        $iFound = 1
                    EndIf
                EndIf

                If $iFound Then
                    ReDim $aResult[UBound($aResult) + 1][4 + $aCols[0]]
                    $aResult[UBound($aResult) - 1][0] = $sDisplayName
                    $aResult[UBound($aResult) - 1][1] = $sDate
;~                     $aResult[ UBound($aResult) - 1][2] = $aKeys[$i]
                    $aResult[UBound($aResult) - 1][2] = $sSubKey ; change back to 3 if keys enabled


                    For $iCol = 1 To $aCols[0]
                        $aResult[UBound($aResult) - 1][3 + $iCol] = RegRead($aKeys[$i] & "\" & $sSubKey, $aCols[$iCol])
                    Next
                EndIf
            EndIf

            $n += 1
        WEnd
    Next

    $aResult[0][0] = UBound($aResult) - 1
    Return $aResult
EndFunc   ;==>_UninstallList

; #FUNCTION# ====================================================================================================================
; Name ..........: _RegEnumKeyEx
; Description ...: Enumerates the subkeys of the specified open registry key. The function retrieves information about one subkey
;                  each time it is called.
; Syntax ........: _RegEnumKeyEx($sKey, $iInstance)
; Parameters ....: $sKey                - The registry key to read.
;                  $iInstance           - The 1-based key instance to retrieve.
; Return values .: Success              - A 1D array :
;                                          $aArray[0] = subkey name
;                                          $aArray[1] = time at which the enumerated subkey was last written
;                  Failure               - Returns 0 and set @eror to non-zero value
; Author ........: jguinch
; ===============================================================================================================================
Func _RegEnumKeyEx($sKey, $iInstance)
    If Not IsDeclared("KEY_WOW64_32KEY") Then Local Const $KEY_WOW64_32KEY = 0x0200
    If Not IsDeclared("KEY_WOW64_64KEY") Then Local Const $KEY_WOW64_64KEY = 0x0100
    If Not IsDeclared("KEY_ENUMERATE_SUB_KEYS") Then Local Const $KEY_ENUMERATE_SUB_KEYS = 0x0008

    If Not IsDeclared("tagFILETIME") Then Local Const $tagFILETIME = "struct;dword Lo;dword Hi;endstruct"

    Local $iSamDesired = $KEY_ENUMERATE_SUB_KEYS

    Local $iX64Key = 0, $sRootKey, $aResult[2]

    Local $sRoot = StringRegExpReplace($sKey, "\\.+", "")
    Local $sSubKey = StringRegExpReplace($sKey, "^[^\\]+\\", "")

    $sRoot = StringReplace($sRoot, "64", "")
    If @extended Then $iX64Key = 1

    If Not IsInt($iInstance) Or $iInstance < 1 Then Return SetError(2, 0, 0)

    Switch $sRoot
        Case "HKCR", "HKEY_CLASSES_ROOT"
            $sRootKey = 0x80000000
        Case "HKLM", "HKEY_LOCAL_MACHINE"
            $sRootKey = 0x80000002
        Case "HKCU", "HKEY_CURRENT_USER"
            $sRootKey = 0x80000001
        Case "HKU", "HKEY_USERS"
            $sRootKey = 0x80000003
        Case "HKCC", "HKEY_CURRENT_CONFIG"
            $sRootKey = 0x80000005
        Case Else
            Return SetError(1, 0, 0)
    EndSwitch

    If StringRegExp(@OSArch, "64$") Then
        If @AutoItX64 Or $iX64Key Then
            $iSamDesired = BitOR($iSamDesired, $KEY_WOW64_64KEY)
        Else
            $iSamDesired = BitOR($iSamDesired, $KEY_WOW64_32KEY)
        EndIf
    EndIf

    Local $aRetOPen = DllCall('advapi32.dll', 'long', 'RegOpenKeyExW', 'handle', $sRootKey, 'wstr', $sSubKey, 'dword', 0, 'dword', $iSamDesired, 'ulong_ptr*', 0)
    If @error Then Return SetError(@error, @extended, 0)
    If $aRetOPen[0] Then Return SetError(10, $aRetOPen[0], 0)

    Local $hKey = $aRetOPen[5]

    Local $tFILETIME = DllStructCreate($tagFILETIME)
    Local $lpftLastWriteTime = DllStructGetPtr($tFILETIME)

    Local $aRetEnum = DllCall('Advapi32.dll', 'long', 'RegEnumKeyExW', 'long', $hKey, 'dword', $iInstance - 1, 'wstr', "", 'dword*', 255, 'dword', "", 'ptr', "", 'dword', "", 'ptr', $lpftLastWriteTime)
    If Not IsArray($aRetEnum) Or $aRetEnum[0] <> 0 Then Return SetError(3, 0, 1)

    Local $tFILETIME2 = _Date_Time_FileTimeToLocalFileTime($lpftLastWriteTime)
    Local $localtime = _Date_Time_FileTimeToStr($tFILETIME2, 1)

    $aResult[0] = $aRetEnum[3]
    $aResult[1] = $localtime

    Return $aResult
EndFunc   ;==>_RegEnumKeyEx

 

Edited by Chimaera

Share this post


Link to post
Share on other sites

#14 ·  Posted (edited)

If you read the description in the function header, it says explicitly that a 2D array is returned. So now the question is: which column, or columns, should be searched to find the exceptions used to identify which rows need to be deleted? You then need to fix the code syntax to access the 2D array elements you want to test for exceptions. How is that array syntax meant to be written?

A Clue: Why is this syntax wrong for a 2D array? - $a2D_Array[0]

Edited by czardas

Share this post


Link to post
Share on other sites
;#FUNCTION# 
;Name ..........: _UninstallList
;Return values .: Returns a 2D array of registry keys and values

$aList = _UninstallList(..)   ; so this is a 2D array

For $i = 0 To UBound($aList) - 1
    If NOT StringRegExp($aList[$i], $sExclude) Then   ; $aList[$i] means that you loop through a 1D array

 

Share this post


Link to post
Share on other sites

Managed to squeeze  some time today

im assuming you mean i have to add the extra dimension in the code because it is accessing the array with extra dimension

A Clue: Why is this syntax wrong for a 2D array? - $a2D_Array[0]

Ive been trying various connotations of this to allow a 2 d format like

For $removeloop = UBound($aList)-1 To 1 step -1
    If StringInStr($aList[$removeloop][2], "Microsoft V") Then ; If found...
        _ArrayDelete($aList, $removeloop) ; ...then remove it
    EndIf
Next

;~     If NOT StringRegExp($aList[0][2], $sExclude) Then
;~         $aList[$iIndex] = $aList[2]
;~         $iIndex += 1

But i havent found a solution yet, i even went back and read the array page on the wiki, it seems like it should work but im sure ive over looked something

Finaly, a good filter using a regex with _UnInstallList should do the job ($iSearchMode = 3):

_UninstallList("DisplayName", "(?i)^(?!update for).*", "", 3)

That works but the only downside is i cant allow for multiple removes so i would need to run it over and over again with a diff remove each time 

Unless i can do this 

$sExclude = "Microsoft V*|Security U*|CCC"

Is that possible within regex?

Share this post


Link to post
Share on other sites

#20 ·  Posted (edited)

 

For $removeloop = UBound($aList)-1 To 1 step -1
    If StringInStr($aList[$removeloop][2], "Microsoft V") Then ; If found...
        _ArrayDelete($aList, $removeloop) ; ...then remove it
    EndIf
Next

;~     If NOT StringRegExp($aList[0][2], $sExclude) Then
;~         $aList[$iIndex] = $aList[2]
;~         $iIndex += 1

The syntax in this section looks okay to me, so the problem must be elsewhere.

Edited by czardas

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now