批处理 - 从阵列中删除元素

时间:2016-02-23 20:54:46

标签: arrays batch-file

我相信这个答案应该很简单。我从一个地方获取目录列表并将它们存储到文本文档中。然后我读取文本文档名称并将它们存储到一个数组中。在这个过程结束时,我希望擦除阵列中的所有条目。

我想这样做的原因是因为我要去几个文件夹位置并将它们存储到同一个数组中。然而,当我尝试在以后打印出来的时候,每当它似乎给我各种各样的麻烦时,我都不清理阵列。

每次我填写下一个文件夹时,我都需要一个新的数组。

REM **************************************************************************
                REM this part needs to delete the value but doesnt
                set !array[%arraywiper%]!=0

REM **************************************************************************  

这是我遇到的问题的一个例子。可以运行下面的代码来查看我在说什么。

ECHO OFF & setLocal EnableDELAYedExpansion
cls

REM creates folder for text doc
IF EXIST "c:\TEMP" (echo.) else (md c:\TEMP\)

REM Scans C:\Program Files (x86) and stores into a text doc
(for /f "delims=" %%a in ('dir /a:D-H-S /on /b "C:\Program Files (x86)"') do echo %%a)> c:\TEMP\dork_array_wipe.txt

REM Counts the folders in the text doc
for /d %%a in ("C:\Program Files (x86)\"*) do ( 
set /a locationcount+=1 
)

REM Stores the values from the doc into an array
for /F "usebackq delims=" %%a in ("c:\TEMP\dork_array_wipe.txt") do (
set /A i+=1
call set array[%%i%%]=%%a
)

set arraywiper=1

:Arraywipeloop19

IF %arraywiper%==%locationcount% GOTO Arraywipecomplete19 else (

                    REM Prints array to show value entered array
                    echo array (%arraywiper%): !array[%arraywiper%]!


    REM **************************************************************************
                    REM this part needs to delete the value but doesnt
                    set !array[%arraywiper%]!=0

    REM **************************************************************************              

                    Set /a arraywiper+=1

                    REM Prints out array in question to veryify change took place
                    echo array (%arraywiper%): !array[%arraywiper%]!
                    GOTO Arraywipeloop19
                    )

:Arraywipecomplete19


pause

先生。 Rojo给了我一个很好的解决方案。这是它如何运作的一个例子。

@echo off
setlocal

set "array[0]=foo"
set "array[1]=bar"
set "array[2]=baz"
set "array[3]=qux"
set "array[4]=quux"
set "array[5]=corge"

echo.
echo first printout

echo %array[0]%, %array[1]%, %array[2]%, %array[3]%, %array[4]%, %array[5]%
pause


rem // starting with element 3, delete 2 elements and insert 3 new
call :splice array 3 2 grault garply waldo

echo.
echo second printout

echo %array[0]%, %array[1]%, %array[2]%, %array[3]%, %array[4]%, %array[5]%, %array[6]%, %array[7]%
pause
call :splice array 0 

REM set array[

REM goto :EOF

echo.
echo third printout

echo %array[0]%, %array[1]%, %array[2]%, %array[3]%, %array[4]%, %array[5]%, %array[6]% 

pause

set "array[0]=foo"
set "array[1]=bar"
set "array[2]=baz"
set "array[3]=qux"
set "array[4]=quux"
set "array[5]=corge"

echo.
echo fourth printout

echo %array[0]%, %array[1]%, %array[2]%, %array[3]%, %array[4]%, %array[5]%, %array[6]% 

pause




:splice <array_name> <startIDX> [<deleteCount> [<item1> [<item2> [...]]]]
rem // works like JavaScript Array.prototype.splice()
rem // https://developer.mozilla.org/en-US/search?q=Array.prototype.splice()
setlocal enabledelayedexpansion
set /a idx = 0, argskip = 0, inserted = 0, orig_ubound = -1
if "%~3"=="" (set /a "resume = 1 << 30") else set /a resume = %~2 + %~3
for /f "tokens=1* delims==" %%I in ('set %~1[') do (
    set /a orig_ubound += 1
    if !idx! lss %~2 (
        set "tmp[!idx!]=%%J"
        set /a ubound = idx, idx += 1
    ) else (
        if !inserted! equ 0 (
            for %%# in (%*) do (
                set /a argskip += 1, inserted = 1
                if !argskip! gtr 3 (
                    set "tmp[!idx!]=%%~#"
                    set /a ubound = idx, idx += 1, resume += 1
                )
            )
        )
        if !idx! geq !resume! (
            set "tmp[!idx!]=%%J"
            set /a ubound = idx, idx += 1
        ) else set /a resume -= 1
    )
)
set "r=endlocal"
for /f "tokens=1* delims=[" %%I in ('2^>NUL set tmp[') do (
    set "r=!r!&set "%~1[%%J""
)
for /L %%I in (%idx%,1,%orig_ubound%) do set "r=!r!&set "%~1[%%I]=""
%r%&exit/b

3 个答案:

答案 0 :(得分:3)

以下是我在上面评论的内容的说明,演示了使用setlocalendlocal来忘记变量。

@echo off
setlocal

set idx=0

rem // populate array
setlocal enabledelayedexpansion
for /f "delims=" %%I in ('dir /b /a:-d') do (
    set "array[!idx!]=%%~nxI"
    set /a idx += 1
)

rem // display array
set array[

rem // destroy array
endlocal

rem // result
set array[

或者如果您更喜欢循环遍历数组元素而不是使用set来输出它们的值:

@echo off
setlocal

set idx=0

rem // populate array
setlocal enabledelayedexpansion
for /f "delims=" %%I in ('dir /b /a:-d') do (
    set "array[!idx!]=%%~nxI"
    set /a ubound = idx, idx += 1
)

rem // display array
for /L %%I in (0,1,%ubound%) do echo array[%%I]: !array[%%I]!

rem // destroy array
endlocal

rem // result
echo %array[0]%

编辑:如果您必须使用索引的array-ish方法操作变量集合,我编写了一个与JavaScript Array.prototype.splice()类似的:splice函数。您可以使用它来删除元素,插入元素,两者的组合,甚至可以根据需要清除整个数组。 (只需call :splice arrayname 0取消设置数组中的所有元素。)

@echo off
setlocal

set "array[0]=foo"
set "array[1]=bar"
set "array[2]=baz"
set "array[3]=qux"
set "array[4]=quux"
set "array[5]=corge"

rem // starting with element 3, delete 2 elements and insert 3 new
call :splice array 3 2 grault garply waldo

set array[

goto :EOF

:splice <array_name> <startIDX> [<deleteCount> [<item1> [<item2> [...]]]]
rem // works like JavaScript Array.prototype.splice()
rem // https://developer.mozilla.org/en-US/search?q=Array.prototype.splice()
setlocal enabledelayedexpansion
set /a idx = 0, argskip = 0, inserted = 0, orig_ubound = -1
if "%~3"=="" (set /a "resume = 1 << 30") else set /a resume = %~2 + %~3
for /f "tokens=1* delims==" %%I in ('set %~1[') do (
    set /a orig_ubound += 1
    if !idx! lss %~2 (
        set "tmp[!idx!]=%%J"
        set /a ubound = idx, idx += 1
    ) else (
        if !inserted! equ 0 (
            for %%# in (%*) do (
                set /a argskip += 1, inserted = 1
                if !argskip! gtr 3 (
                    set "tmp[!idx!]=%%~#"
                    set /a ubound = idx, idx += 1, resume += 1
                )
            )
        )
        if !idx! geq !resume! (
            set "tmp[!idx!]=%%J"
            set /a ubound = idx, idx += 1
        ) else set /a resume -= 1
    )
)
set "r=endlocal"
for /f "tokens=1* delims=[" %%I in ('2^>NUL set tmp[') do (
    set "r=!r!&set "%~1[%%J""
)
for /L %%I in (%idx%,1,%orig_ubound%) do set "r=!r!&set "%~1[%%I]=""
%r%&exit/b

答案 1 :(得分:1)

批处理数组也可以通过列表与索引变量结合使用,以将元素与索引值进行匹配

下面使用宏演示了如何对列表类型的数组进行仿真:

  • 修改原始列表或将结果返回到克隆列表
  • 在提供的索引处拼接新元素
  • 按索引号提取特定元素
  • 从列表中删除元素。

每个宏通过以下组合接受Args:

  • 子字符串修改

    • {e}变量名和array变量名替换array clone / return
    • 拼接宏的第二个Arg是要在其中插入新值的索引
  • For循环If else构造

    • 捕获要执行的索引编号。

使用宏和列表(而不是被调用的函数)或将大量值分配给关联的变量可以大大加快执行速度。

Splice宏包含一种验证方法

@Echo off

rem /* Macro Definition - Arg order mandatory in each macro. */
(Set \n=^^^
%= Newline var for macro definitions. Do Not Modify =%
)
::: Reserved Variable names used for Macro's:
::: \n Splice Extract Remove Groupname Insert Prepend Append Rvar RV cArr
::: {i} {i#} Tokens

rem /* Usage: %Splice:{e}={ListVarName}{Insertion Index}{OPTIONAL-NewListVarname}% */
Set Splice=For %%n in (1 2)do if %%n==2 (%\n%
 For /F "Tokens=1,2,3 Delims={}" %%1 in ("{e}") Do (%\n%
  Set "Groupname=%%1"%\n%
  Set "{i#}=%%2"%\n%
  Set "cArr=%%3"%\n%
 )%\n%
 Set "Prepend="%\n%
 Set "Append="%\n%
 Set "{i}=0"%\n%
 For %%G in (!Groupname!)Do For %%E in (!%%G!)Do (%\n%
  If !{i}! LSS !{i#}! (%\n%
   If "!Prepend!" == "" (Set "Prepend=%%E")Else (Set "Prepend=!Prepend! %%E")%\n%
  )%\n%
  If !{i}! GEQ !{i#}! (%\n%
   If "!Append!" == "" (Set "Append=%%E")Else (Set "Append=!Append! %%E")%\n%
  )%\n%
  Set /A "{i}+=1"%\n%
 )%\n%
  If Not "!cArr!" == "" (Set "!cArr!=!Prepend! !Insert! !Append!")Else (Set "!Groupname!=!Prepend! !Insert! !Append!")%\n%
)Else Set Insert=

rem /* Usage: %Remove:{e}={ListVarName}{OPTIONAL-NewListVarname}% Index/es to be removed as Integer list */
Set Remove=For %%n in (1 2)do if %%n==2 (%\n%
 For /F "Tokens=1,2 Delims={}" %%1 in ("{e}") Do (%\n%
  Set "Groupname=%%1"%\n%
  Set "cArr=%%2"%\n%
  Set "Rvar="%\n%
 )%\n%
 Set "Prepend="%\n%
 Set "Append="%\n%
 Set "{i}=0"%\n%
 For %%G in (!Groupname!)Do For %%E in (!%%G!)Do (%\n%
  Set /A "{i}+=1"%\n%
  Set "{i#}=1"%\n%
  For %%t in (!Tokens!)Do If %%t EQU !{i}! Set "{i#}=0"%\n%
  If !{i#}! EQU 1 (%\n%
   If "!Rvar!" == "" (Set "Rvar=%%E")Else (Set "Rvar=!Rvar! %%E")%\n%
  )%\n%
 )%\n%
 If Not "!cArr!" == "" (Set "!cArr!=!Rvar!")Else (Set "!Groupname!=!Rvar!")%\n%
)Else Set Tokens=

rem /* Usage: %Extract:{e}={ListVarName}{OPTIONAL-ReturnVarName[Default - RV ]}%Index/es to be extracted as Integer list */
Set Extract=For %%n in (1 2)Do if %%n==2 (%\n%
 For /F "Tokens=1,2 Delims={}" %%1 in ("{e}") Do (%\n%
  Set "Groupname=%%1"%\n%
  Set "Rvar=%%2"%\n%
  If "!Rvar!" == "" ( Set "Rvar=RV" )%\n%
 )%\n%
 Set "{i}=0"%\n%
  Set "!Rvar!="%\n%
 For %%G in (!Groupname!)Do For %%E in (!%%G!)Do (%\n%
  Set /A "{i}+=1"%\n%
  For %%v in (!{i#}!)Do If !{i}! EQU %%v (%\n%
   For %%r in (!Rvar!)Do If "!%%r!" == "" ( Set "!Rvar!=%%E" )Else ( Set "!Rvar!=!%%r! %%E" )%\n%
  )%\n%
 )%\n%
)Else Set {i#}=

rem /* Delayed Expansion Must be enabled to expand macro's */ ::: EXAMPLE USAGES:
Setlocal EnableDelayedExpansion
Set List=1 2 3 4 5 6 7 8
Set list
Echo/rem /* insert new elements. Example = insert "9 9" after the 3rd element. */
%Splice:{e}={List}{3}%9 9
Set List
Echo/rem /* extract element/s. example = 1st 4th and 6th elements */
%Extract:{e}={List}{YourReturnVar}%1 4 6
Set YourReturnVar
Echo/rem /* remove elements from list. example=remove the 1st 4th and 6th elements */
%Remove:{e}={List}%1 4 6
Set List
Echo/rem /* Clone existing list; inserting elements . Example = insert previously extracted elements
Echo/rem    at the max index [ from the last {i} index returned ] */
%Splice:{e}={List}{!{i}!}{clone}%!YourReturnVar!
Set Clone

示例输出:

Output of Example

答案 2 :(得分:0)

我无法添加注释,但应提及以下对上述答案的限制......

当您在设置数组的行中的代码中远程使用set array[ & endlocal时,除非在被调用函数内使用endlocal,否则它可能还会擦除与之前设置的数组变量无关的所有其他变量,其中唯一的变量set是属于数组的那个。如果该限制为真,则应在答案中提及。

这引起了人们的担忧,是否可以在一个代码周围普遍使用ENDLOCAL来擦除目标变量,以及上述方法是否通用。