Board logo

标题: 一个关于随机获取文件名的问题. [打印本页]

作者: voiL     时间: 2006-7-26 19:27    标题: 一个关于随机获取文件名的问题.

运行环境:XP SP2 CMD.

这是一个朋友询问我的问题,觉得挺有趣,但由于水平有限,我又写不出来这样的批处理.

题目是这样的:

条件:获取的名字必须是随机的,不能太有规律(不能是完全按大小或名字或建立日期等),而且有数量限制(以100个文件为例).因为以大小或名字等排列方式,我自己就写出来了,难倒我的就是随机100个文件这里.

    在MP3目录下,我有数目N的MP3文件(基本都是以中文命名的),现在我想用批处理随机生成一个M3U列表,然后调用WMP来播放,以实现WMP的随机播放.

其它的我都能写,就单单是随机获取文件名这个步骤我写不出来.

不知论坛上有没感兴趣的朋友,麻烦给出个代码.

[ Last edited by voiL on 2006-7-26 at 19:37 ]
作者: voiL     时间: 2006-7-26 19:38
对了,如果BAT实现不了的话,可以考虑VBS,因为我对VBS不熟,没办法用VBS来写.
作者: namejm     时间: 2006-7-26 19:40
  用%random%和findstr /n 估计能行,主要思路就是先用findstr /n 来给MP3文件名列表编号,然后用%random%来生成随机数,然后把%random%所代表的文件名列出来。细节问题,暂时还没有考虑到,具体的代码也还没有写出来,先写个思路在这里,看各位还有什么好的思路没有。
作者: voiL     时间: 2006-7-26 20:48
呵呵,%random%这些参数太深奥了,还没完全弄懂,所以还没办法写出来,高手见笑了.
作者: zh159     时间: 2006-7-26 20:53
WMP、WINAMP不是有随机播放功能么?这样做有点罗嗦了吧^_^
作者: voiL     时间: 2006-7-26 21:00


  Quote:
Originally posted by zxcv at 2006-7-26 20:53:
WMP、WINAMP不是有随机播放功能么?这样做有点罗嗦了吧^_^

我感兴趣的并不是它是否能实现随机播放,而是怎么去实现随机获取文件名的办法而已.由于自己搞不定,没办法之下才来请教的.
作者: voiL     时间: 2006-7-26 21:01
我曾经也想过通过名字、日期、大小来分别排列,然后各取前面的部分文件名来再进行名字方式排列的,但这方法只能用一次,因为每一次生成的名字还是一样的,不能实现随机取得文件名.
作者: zh159     时间: 2006-7-26 21:08
我的办法:
set Name%NN%=文件名(NN为1-99)

%random:~-2%获取后两位随机数,根据后两位随机数得到%NameNN%

[ Last edited by zxcv on 2006-7-26 at 23:40 ]
作者: 无奈何     时间: 2006-7-26 21:52
随机排序的简单做法是在文本行前增加 %random% 随机数,然后 sort 排序一下。
试试下面的脚本是否满足需要,此脚本的特点是不产生临时文件。

  Quote:

  1. @echo off
  2. if "%1" NEQ "$" (
  3.         for /f "tokens=1,2 delims=:" %%a in ('"%~0" $^|sort') do @echo %%b
  4. ) else for /f %%i in ('dir /b /a-d') do @call :sub %%i
  5. goto :EOF
  6. :sub
  7. echo %random%:%*
  8. goto :EOF
        无奈何发表于    2006-07-26  21:43


作者: voiL     时间: 2006-7-27 00:08
无奈何兄,你给出的方法我试过,运行结果与我之前所写的也所差无几,不能实现真正意义上的随机,因为每一次运行后所得到的结果都是一样的.

而且在输出的文件名前面都会有一组长度不一的"编号",我想这些"编号"的长度不一与文件名的不规则,对于后面的程序来说也是个问题.
作者: doscc     时间: 2006-7-27 02:11
改进后代码:
增加了:
"自动获得 文件数量 跟拒文件个数 设置随机数"
"进度显示"
@echo off
dir /b *.mp3 | findstr /n "." >tem.txt
for /f "tokens=1 delims=:" %%g in (tem.txt) do set maxnum=%%g
copy nul list.txt >NUL
set "y="
set "n="

:s
set r=%random:~-1%%random:~-1%%random:~-1%
set /a r*=1
if "%r%" GTR "%maxnum%" goto s
for /f "tokens=1 delims=:" %%i in ('type list.txt ^| findstr /n "."') do if "%%i"=="100" del tem.txt & exit
echo %y% | findstr /r "\<%r%\>" >NUL || set y=%y% %r% & call :ls %r%

goto s

:ls
set num=%1
for /f "tokens=1,2* delims=:" %%a in (tem.txt) do (if "%%a"=="%num%" echo %%b >> list.txt && set /a n+=1 & title 以完成 %n% %% ....)
goto :EOF
[ Last edited by doscc on 2006-7-27 at 21:53 ]
作者: voiL     时间: 2006-7-27 05:45
经初步测试,doscc兄的代码确实有效,每次生成的列表都不一样的.

谢谢doscc兄.
作者: zh159     时间: 2006-7-27 15:07
doscc 效率比较低,问题出在:

  Quote:
:ls
set num=%1
for /f "tokens=1,2* delims=:" %%a in ('type tem.txt') do if "%%a"=="%num%" echo %%b >> list.txt
goto :EOF

这段,%random%产生随机数后,每个随机数都要“type tem.txt”一次,搜索到"%%a"=="%num%"行才写入List.txt;如果生成的随机数“%num%”不在“type tem.txt”范围内,又得再生成随机数

我写了一段,速度快些:

  Quote:
@echo off
copy nul List.txt >NUL
setlocal EnableDelayedExpansion
for /f "delims=" %%a in ('dir/b/s *.mp3^|find ".mp3"') do (    搜索当前目录下所有文件夹(可不用)
    set Name=%%a
    set /a N=!N! + 1
    set Name!N!=!Name:%cd%=.!)    将“dir/s”生成的当前目录替换为“.”,没有“dir/s”就会自动忽略

echo.
echo   随机生成列表,请稍等...
:loop
set N=%random:~-3%    ≥100个以上文件为3,<100个以下为2(位数越高速度越慢)
if "%N%" == "000" goto loop    ≥100个以上文件为000,<100个以下为00
if "%N:~0,1%" == "0" set N=%N:~1%
if "%N:~0,1%" == "0" set N=%N:~1%
if %N% GTR 137 goto loop    改为文件总数量

set /a M=!M!+1
echo echo %%Name!N!%%^>^>List.txt>Temp.bat
call Temp.bat
if %M% GEQ 300 del Temp.bat & exit    生成列表的行数
goto loop

将名称 set 变量为 NameN,用”if %N% GTR 137 goto loop“控制%random%在文件数量范围内生成随机数变量N(“if "%N:~0,1%" == "0" set N=%N:~1%”去处随机数前面的“0”),用Name%N%写入列表;

以下一段自动检测文件数量设置参数

  Quote:
@echo off
set DIR=dir/s    “dir”为只搜索当前目录,“dir/s”为搜索当前目录下所有文件夹
set 生成列表行数=200

for /f "tokens=1 delims= " %%i in ('%DIR% *.mp3^|find "个文件"') do set 文件数量=%%i
if %文件数量% GEQ 100 set X=3
if %文件数量% GEQ 100 set NN=000
if %文件数量% LSS 100 set X=2
if %文件数量% LSS 100 set NN=00
if %文件数量% LSS 10 set X=1
if %文件数量% LSS 10 set NN=0
copy nul List.txt >NUL
setlocal EnableDelayedExpansion
for /f "delims=" %%a in ('%DIR%/b *.mp3^|find ".mp3"') do (
    set Name=%%a
    set /a N=!N! + 1
    set Name!N!=!Name:%cd%=.!)

echo.
echo   随机生成列表,请稍等...

echo :loop>Loop.bat
echo set N=%%random:~-%X%%%>>Loop.bat
echo if "%%N%%" == "%NN%" goto loop>>Loop.bat
echo if "%%N:~0,1%%" == "0" set N=%%N:~1%%>>Loop.bat
echo if "%%N:~0,1%%" == "0" set N=%%N:~1%%>>Loop.bat
echo if %%N%% GTR %文件数量% goto loop>>Loop.bat
:loop
call Loop.bat
set /a M=!M!+1
echo echo %%Name!N!%%^>^>List.txt>Temp.bat
call Temp.bat
if %M% GEQ %生成列表行数% goto :End
goto loop

:End
del Loop.bat
del Temp.bat
exit

[ Last edited by zxcv on 2006-7-27 at 15:58 ]
作者: namejm     时间: 2006-7-27 17:54
  11楼的代码存在一个缺憾:在列表中不能排除重复的文件名。

  以下代码可以避免这一缺憾,主要思想是:先用for+findstr /n来计算要操作的文件夹下mp3文件的个数max,然后用%random%生成随机的三位数num(假设文件的最大个数为999个);如果num大于max,则重新生成随机数num;如果num小于或等于max,且第num行的文件名还没有加入list.txt,则把编号为num的文件名写入list.txt,同时把此编号写入num.txt;如果此时num在num.txt中出现,则重新检测num……

  因为有大量的文本查询过程,此代码的效率比较低,执行的时候请耐心等待:
@echo off
if exist list.txt del /q list.txt
if exist num.txt del /q num.txt
echo 已经列表的曲目编号:>num.txt
set _time=1
for /f "tokens=1 delims=:" %%i in ('dir /a-d /b "要操作的文件夹\*.mp3"^|findstr /n .') do set max=%%i
:loop
set num=%random:~-3%
set /a num=1%num%-1000
if %num% leq 0 goto loop
if %num% gtr %max% goto loop
for /f %%i in (num.txt) do if %%i equ %num% goto loop
for /f "tokens=1,2* delims=:" %%i in ('dir /a-d /b "要操作的文件夹\*.mp3"^|findstr /n .') do (
  if %%i equ %num% (
    echo %%i>>num.txt & echo %%j>>list.txt && set /a _time+=1 && if %_time% lss %max% goto loop
  )
)
del /q num.txt
start list.txt
[ Last edited by namejm on 2006-7-28 at 09:00 ]
作者: doscc     时间: 2006-7-27 20:38
实现楼主的要求 写出来的代码效率是比较底的

我的代码 应该不会出现重复的行

因为在下面这一行做了过虑

echo %y% | findstr /r "\<%r%\>" >NUL || set y=%y% %r% & call :ls %r%
y 记录每个不一样的 随机数

当 r 不在 y 中出现时 就 记录 r 并 调用 :ls 把 tem.txt 中的第 r 行 追加到 list.txt 里
作者: zh159     时间: 2006-7-27 20:38
重新编了一个,不用文本查询,清除写入过列表的变量来判断循环生成随机数
137个文件30秒内完成(随机数运气好的话速度就快,越往后有效变量越少越慢):
红色部分不要

  Quote:
pause
cls
@echo off
copy nul List.txt >NUL
setlocal EnableDelayedExpansion
for /f "delims=" %%a in ('dir/b *.mp3^|find ".mp3"') do (
    set /a N=!N! + 1
    set Name!N!=%%a)

echo.
echo   随机生成列表,请稍等...
echo.
echo   序号 - 随机文件名
echo.
:loop
set N=%random:~-3%
if "%N%" == "000" goto loop
if "%N:~0,1%" == "0" set N=%N:~1%
if "%N:~0,1%" == "0" set N=%N:~1%
if %N% GTR 137 goto loop“137”文件数量
echo set Name=%%Name!N!%%>Temp.bat判断改变量是否被清除
call Temp.bat
if "%Name%" == "" goto loop

set /a M=%M%+1
echo echo   %%M%% - %%Name!N!%%>Temp.bat显示序号 - 随机文件名
echo echo %%Name!N!%%^>^>List.txt>>Temp.bat
call Temp.bat
set Name!N!=清除已写入列表的变量
if %M% GEQ 137 goto End“137”文件数量
goto loop

:End
del Temp.bat
echo.
echo   完成!
echo.
pause
exit

好像有个小毛病:有时候会失败退出:(

[ Last edited by zxcv on 2006-7-28 at 01:19 ]
作者: zh159     时间: 2006-7-27 20:57
来一个自动判断文件数量设置随机位数的:

  Quote:
@echo off
pause
cls

for /f "tokens=1 delims= " %%i in ('dir *.mp3^|find "个文件"') do set 文件数量=%%i
if %文件数量% GEQ 100 set X=3
if %文件数量% GEQ 100 set NN=000
if %文件数量% LSS 100 set X=2
if %文件数量% LSS 100 set NN=00
if %文件数量% LSS 10 set X=1
if %文件数量% LSS 10 set NN=0
copy nul List.txt >NUL
setlocal EnableDelayedExpansion
for /f "delims=" %%a in ('dir/b *.mp3^|find ".mp3"') do (
    set /a N=!N! + 1
    set Name!N!=%%a)

echo.
echo   随机生成列表,请稍等...
echo.
echo   序号 - 随机(%文件数量% 个文件)
echo.

echo :loop>Loop.bat
echo set N=%%random:~-%X%%%>>Loop.bat
echo if "%%N%%" == "%NN%" goto loop>>Loop.bat
echo if "%%N:~0,1%%" == "0" set N=%%N:~1%%>>Loop.bat
echo if "%%N:~0,1%%" == "0" set N=%%N:~1%%>>Loop.bat
echo if %%N%% GTR %文件数量% goto loop>>Loop.bat
echo echo set Name=%%%%Name%%N%%%%%%^>Temp.bat>>Loop.bat
echo call Temp.bat>>Loop.bat
echo if "%%Name%%" == "" goto loop>>Loop.bat

:loop
call Loop.bat
set /a M=!M!+1
echo echo   %%M%% - %%Name!N!%%>Temp.bat
echo echo %%Name!N!%%^>^>List.txt>>Temp.bat
call Temp.bat
set Name!N!=
if %M% GEQ %文件数量% goto :End
goto loop

:End
del Loop.bat
del Temp.bat
echo.
echo   完成!
echo.
pause
exit


作者: voiL     时间: 2006-7-27 21:44
有这么多朋友的代码相信已经足够矣...

我拿下来慢慢研究一下,希望能拼凑个更快更有效率的出来...^_^

谢谢各位...
作者: 无奈何     时间: 2006-7-27 22:20
Re voiL
在我这里每次的执行结果都不是一样的,并且没有文件名前的"编号"。麻烦贴一下结果我看看。
作者: zh159     时间: 2006-7-27 22:42
无奈何版主的不错,速度也快
不过有空格的文件名断了(不完整)应该可以些改一下
作者: namejm     时间: 2006-7-27 23:00


  Quote:
Originally posted by doscc at 2006-7-27 20:38:
我的代码 应该不会出现重复的行
因为在下面这一行做了过虑
echo %y% | findstr /r "\<%r%\>" >NUL || set y=%y% %r% & call :ls %r%
y 记录每个不一样的 随机数
当 r 不在 y 中出现时 就 记录 r 并 调用 :ls 把 tem.txt 中的第 r 行 追加到 list.txt 里

  大概弄明白了你的过滤规则。

  你在16楼的代码有问题,不知道红字部分是注释还是代码;如果是代码的话,执行完毕之后,list.txt文件里会有为数众多的行内容为“   清除已写入列表的变量”;如果是注释的话,把注释部分清除掉之后,list.txt中为数众多的行内容为"ECHO 处于关闭状态。"

  你说这段代码有时候会失败退出,估计是要处理的行数太多,导致name!N!变量太多,而CMD中变量的数量好象是有限制的。

[ Last edited by namejm on 2006-7-28 at 09:13 ]
作者: namejm     时间: 2006-7-27 23:09


  Quote:
Originally posted by zxcv at 2006-7-27 22:42:
无奈何版主的不错,速度也快
不过有空格的文件名断了(不完整)应该可以些改一下

  无奈何版主的代码,除了9楼有一处之外,在什么地方还有哦?也想拿来跑一下。
作者: doscc     时间: 2006-7-27 23:40


  Quote:
Originally posted by namejm at 2006-7-27 23:00:


  大概弄明白了你的过滤规则。

  你在16楼的代码有问题,不知道红字部分是注释还是代码;如果是代码的话,执行完毕之后,list.txt文件里会有为数众多的行内容为“   清除已写入列表的变量”;如果是注释的话,list.txt中为数众多的行内容为"ECHO 处于关闭状态。"

  你说这段代码有时候会失败退出,估计是要处理的行数太多,导致name!N!变量太多,而CMD中变量的数量好象是有限制的。你在16楼的代码有问题,不知道红字部分是注释还是代码;如果是代码的话,执行完毕之后,list.txt文件釠...

16 楼是 zxcv 兄的代码.

11楼 则是我的代码. 以作更新!
作者: 无奈何     时间: 2006-7-27 23:51
Re  zxcv
谢谢指正,含空格的文件名确实有问题,简单修正一下。
Re namejm
请见本楼修正代码。

  Quote:

  1. @echo off
  2. if "%1" NEQ "$" (
  3.         for /f "tokens=1,2 delims=:" %%a in ('"%~0" $^|sort') do @echo %%b
  4. ) else for /f "delims=" %%i in ('dir /b /a-d') do @call :sub %%i
  5. goto :EOF
  6. :sub
  7. echo %random%:%*
  8. goto :EOF
        无奈何发表于    2006-07-27  23:44


作者: zh159     时间: 2006-7-28 00:00
无奈何版主代码的速度很快,137个文件只用了不到3秒

  Quote:
@echo off
copy nul List.txt >NUL

if "%1" NEQ "$" (

        for /f "tokens=1,2 delims=:" %%a in ('"%~0" $^|sort') do @echo %%b>>List.txt

) else for /f "delims=" %%i in ('dir /b /a-d *.mp3') do @call :sub %%i

goto :EOF

:sub

echo %random%:%*

goto :EOF

[ Last edited by zxcv on 2006-7-28 at 00:02 ]
作者: namejm     时间: 2006-7-28 00:50
Re doscc:

  sorry,把你的代码看错了,不好意思。
作者: zh159     时间: 2006-7-28 01:11


  Quote:
Originally posted by namejm at 2006-7-27 23:00:


  大概弄明白了你的过滤规则。

  你在16楼的代码有问题,不知道红字部分是注释还是代码;如果是代码的话,执行完毕之后,list.txt文件釠...

后面文字为注悉,BAT里面不要

name!N!变量我试过548都没问题,估计是
echo set Name=%%Name!N!%%>Temp.bat
call Temp.bat
这部分有时候来不及反应

[ Last edited by zxcv on 2006-7-28 at 01:18 ]
作者: namejm     时间: 2006-7-28 09:12
Re zxcv:

  如果你红色部分为注释的话,这样的注释格式是错误的,有的地方还会引起执行错误,比如 set Name!N!=清除已写入列表的变量 这一句,会把"清除已写入列表的变量"这个值赋给Name!N!这个变量,希望你能修正。
作者: namejm     时间: 2006-7-28 09:25
  无奈何版主25楼的代码写得简洁至极,同时也让人费解至极,看了老半天,还是云里雾里的,难道真的像他以前所声称的那样,代码要写得尽量简洁、尽量晦涩?如果只是简洁,那可是我等的福气;如果再加上“尽量晦涩难懂”,我的妈呀,估计要晕倒一片像我这样的菜菜。希望无奈何版主能把你的代码稍微点拨一下,以便让我们对你的思路有所了解,光有9楼的只言片语我觉得还不过瘾。

  发现了一个有趣的现象:如果把24楼代码打头的那句@echo off去掉,list.txt的内容将会大大增加,增加的内容形如"\当前文件夹名>echo 20454",一般而言,如果文件有N个,这样的内容也回增加N行,按理说@echo off语句只是起屏蔽回显的作用,但是在这段代码中竟然会影响执行结果,有点匪夷所思。顺便提一下,代码中的@echo %%b>>List.txt和@call :sub %%i中的@去掉之后似乎并不影响代码执行时候的表现,不知道无奈何版主为何还要保留@。

[ Last edited by namejm on 2006-7-28 at 10:59 ]
作者: zh159     时间: 2006-7-28 09:42


  Quote:
Originally posted by namejm at 2006-7-28 09:12:
Re zxcv:

  如果你红色部分为注释的话,这样的注释格式是错误的,有的地方还会引起执行错误,比如 set Name!N!=清除已写入列表的变量 这一句,会栮..

说实话,那是我说明本行的作用发贴时加上去的,真正的BAT中本来就没有(所以我的说明:红色部分不要);不过你应该可以看得懂那些是不要的啊

俺不太清楚怎样在后面加上不影响代码的注悉
作者: zh159     时间: 2006-7-28 09:48
voiL 在 10 楼说的无奈何版主代码"编号"问题,估计 voiL 是在“echo %random%:%*”一行后面>>List.txt了应该是在“@echo %%b”后面>>List.txt
看 25 楼
作者: 220110     时间: 2006-7-28 10:01
获取目录下所有的mp3文件数量,用 for /r %%i in (*.mp3) do set /a n+=1 来取代:

[1] for /f "tokens=1 delims= " %%i in ('dir *.mp3^|find "个文件"') do set 文件数量=%%i

[2] dir /b *.mp3 | findstr /n "." >tem.txt
     for /f "tokens=1 delims=:" %%g in (tem.txt) do set maxnum=%%g

[3] for /f "delims=" %%i in ('dir /b /a-d') do @call :sub %%i

是否效率会高点?请willssort作下深入剖析.
作者: namejm     时间: 2006-7-28 10:32


  Quote:
Originally posted by zxcv at 2006-7-28 09:42:

说实话,那是我说明本行的作用发贴时加上去的,真正的BAT中本来就没有(所以我的说明:红色部分不要);不过你应该可以看得懂邠...

  如果只是加注释,可以在语句的上一行或下一行用 :: 注释内容 或者 rem 注释内容 的格式。

[ Last edited by namejm on 2006-7-28 at 10:35 ]
作者: namejm     时间: 2006-7-28 14:16
  和bagpipe探讨了无奈何版主25楼的代码,发现这段代码还有可精简的地方,把bagpipe精简后的代码发一下:

1、
@echo off
setlocal enabledelayedexpansion
copy nul List.txt >NUL
if "%1" NEQ "$" (
    for /f "tokens=1,2 delims=:" %%a in ('"%~0" $^|sort') do echo %%b>>List.txt
) else for /f "delims=" %%i in ('dir /b /a-d *.mp3') do echo !random!:%%i
2、
@echo off
copy nul List.txt >NUL
if "%1" NEQ "$" (
    for /f "tokens=1,2 delims=:" %%a in ('"%~0" $^|sort') do echo %%b>>List.txt
) else for /f "delims=" %%i in ('dir /b /a-d *.mp3') do call echo %%random%%:%%%%i

作者: doscc     时间: 2006-7-28 16:13


  Quote:
Originally posted by 无奈何 at 2006-7-27 23:51:
@echo off

if "%1" NEQ "$" (

        for /f "tokens=1,2 delims=:" %%a in ('"%~0" $^|sort') do @echo %%b

) else for /f "delims=" %%i in ('dir /b /a-d') do @call :sub %%i

goto :EOF

:sub

echo %random%:%*

goto :EOF


无奈兄 的代码 真的很高明! 不愧是版主!

高明之处 ('"%~0" $^|sort') 和 echo %random%:%*   前乎后应也!

简单解析一下代码:
获得所有文件 在每个文件前标上 随机数 再排列 过虑输出!

[ Last edited by doscc on 2006-7-28 at 16:18 ]
作者: doscc     时间: 2006-7-28 16:22
Re: namejm & bagpipe
call echo %%random%%:%%%%i 这句用得很好!
作者: willsort     时间: 2006-7-28 19:49


  Quote:
Originally posted by 220110 at 2006-7-28 10:01:
获取目录下所有的mp3文件数量,用 for /r %%i in (*.mp3) do set /a n+=1 来取代:

[1] for /f "tokens=1 delims= " %%i in ('dir *.mp3^|find "个文件"') do set 文件数量=%%i

[2] dir /b *.mp3 | findstr /n "." >tem.txt
     for /f "tokens=1 delims=:" %%g in (tem.txt) do set maxnum=%%g

[3] for /f "delims=" %%i in ('dir /b /a-d') do @call :sub %%i

是否效率会高点?请willssort作下深入剖析.

Re 220110『第 32 楼』:  

      关于上述几段代码的问题,我有以下几点浅见:

      1、从算法角度上来看,各段代码的时间复杂度是一致的,所以他们真正的运行效率主要取决于系统支持上的细节;

      2、从代码角度上来看,兄的代码效率上的优势在于没有使用文本的的输入输出操作;而众所周知的是,I/O向来是影响代码效率的瓶颈之一,这是其他的几段代码所不及的。在其他的代码中,代码[1]的效率应该强于代码[2],因为代码[2]不仅对dir的输出文本作了编辑(加行号),而且for/f的文本处理量也明显大于代码[1];至于代码[3]的用法,因为涉及到了批处理的递归调用(其本质是数据读取),所以其效率也低于代码[1]。

      3、但在实际的测试中,结果并不完全像2所分析的那样。在小规模的测试中(Audio目录,文件数746),兄的代码效率最高(140ms),其次却并非代码[1](234ms),而是代码[2](218ms)。分析其结果,原因就在于,代码[2]并非直接用for/f分析findstr的结果,而是使用了临时文件temp.txt,而操作系统为了改善文件IO的效率,会在内存中开辟出相应的文件IO缓冲区,以提高文件的访问速度,而代码[1]因为无法“享受到这种优惠政策”而导致了初段赛跑的落后。这是一个典型的因为系统特性而影响代码效率的例子。

      4、而在一些大规模的测试中(整盘测试,文件数>60000),情况再次发生了变化。首次的测试结果是,兄的代码>代码[1]>代码[2],但是随后的测试中,情况就变成了代码[1]>兄的代码>代码[2]。其原因在于操作系统针对dir等大规模的文件I/O行为设计了位于硬盘上的预存取(Prefetch)机制,由于兄的代码无法“借光”,所以在旗开得胜之后便一蹶不振了。而代码[2]的因为规模的增大暴露出其文件处理算法上的落后。这是另一个比较典型的因为系统特性而影响代码效率的例子。
      
      5、在实用角度上来看,兄的代码因为for不遍历隐藏和系统属性的文件,所以其结果会在一些复杂的应用场合中发生偏差。代码[2]中因为缺少/a-d开关,因为会将目录也计入文件数中。另外,如果在代码[1]的dir开关中,加入/w会对效率的提升有些帮助,而加入开关/-c,则可以去除结果中的逗号分隔符。而如果将代码[1]改为代码[2]的临时文件机制,则效率可以得到进一步提升。所以,综合各方案的我的修改后的方案如下:
@echo off & setlocal
dir %1 /a/s/w/-c | findstr "个文件">temp.txt
for /f %%i in (temp.txt) do set filenum=%%i
echo.FileNum:%filenum%
if exist temp.txt del temp.txt
[ Last edited by willsort on 2006-7-29 at 01:27 ]
作者: 220110     时间: 2006-7-29 00:23
Re willsort:
不知道你有没有测试深层多层子目录下的情况?
总觉得for /r  比 dir /b /a:-d  来得效率要高,排除你的第五点提的系统属性影响。
我只在根目录对系统盘测试了下,系统盘算足够多的文件和足够深的路径了吧;暂无其它新测试办法和环境对比。

Re namejm & bagpipe:
你们确实不错!还能精简。
只是,你们忽略了无奈何的初衷——不使用临时文件。
看你们不用临时文件,还能怎么精简!!激将一下,哈

[ Last edited by 220110 on 2006-7-29 at 00:25 ]
作者: willsort     时间: 2006-7-29 01:46
Re 220110:

      在你回复之前,就开始对原稿进行重新编辑,之后才看到你的回复,其中对你提到的新问题略有提及。

      至于 namejm & bagpipe 兄 对 无奈何兄的代码精简,其实并未涉及临时文件,代码中的list.txt,实际上是保存结果数据的文件,而无奈何兄的原方案是直接将他们输出至控制台上的。

      而无奈何兄的让namejm兄所难以理解的if/lse结构,才真正是避免临时文件的关键。也就是说,如果使用临时文件,则可以不使用if/lse结构,比如我编辑过的以下代码。

      另外,无奈何兄的代码最突出的特点,不在于简洁或者晦涩,而在于其反其路而行之的高效算法,不再去控制随机数的生成,而是去控制列表的生成,这是其它几段代码都无法比及的。
@echo off
cd.>temp.txt & cd.>list.txt
for /f "delims=" %%i in ('dir *.mp3 /b/a-d') do call echo %%random%%%%random%%:%%i>>temp.txt
sort < temp.txt > temp1.txt
for /f "tokens=1,2 delims=:" %%a in (temp1.txt) do echo %%b>>list.txt
for %%f in (temp?.txt) do del %%f
[ Last edited by willsort on 2006-7-29 at 01:48 ]
作者: namejm     时间: 2006-7-29 20:44
  看了willsort版主39楼的代码,才知道制造空文本文件还可以用cd.>这样的方法,开眼了。

  无奈何版主的代码确实很高明,抛开按顺序打印行号之后再乱序选取的常规思路,而直接乱序命名行号再重列,是乱中求顺而达到乱序排列的目的,此法的效率和常规解法的效率不是同一个数量级的。不过,因为调用自身的缘故,去掉开头的@echo off一句之后,就会得到错误的结果,因此,这段代码只能作为一个独立的脚本来使用,而不能作为某个脚本中内部的子段来调用。如果能做出可循环调用的子段的话,就十分完美了。

  搭个车问个问题:重定向符号<和>在同一条语句中接连使用的方法,不怎么熟悉,哪位能讲解一下不?主要讲一下使用环境、有没有什么条件的限制等。
作者: 无奈何     时间: 2006-7-29 22:17
还是 willsort 兄的总结最点到要害。
不好意思的说,我给出的代码我并没有太多的关注效率问题,只是不想生成临时文件。这段代码只是一个典型的递归调用的例子,看讨论已经解释的比较清楚了,不再多言。如果在不在意生成临时文件的情况下 willsort 兄 39 楼给出的代码无疑是最简单且容易操控的。
关于 namejm 兄与 bagpipe 兄的精简我是很喜欢这种精神的,可以改善代码的质量,提高代码的编写水平,并且不时还会有新的发现。当然这其中会有代码书写习惯的问题,就像我非不得以的情况我会尽量避开延缓环境变量的使用,而更喜欢调用外部代码段,这样的程序结构看起来更好看一点,效率问题我还没有深入的研究过。再者 namejm 兄  34 楼 代码段 2、中 %%random%%:%%%%i 句是有问题的,当含有 1.mp3、2.mp3 这样的文件时文件名会丢失,原因是参数 i 多次被转义,修正是去掉两个 % ,参见 39 的代码。
作者: HUNRYBECKY     时间: 2006-12-10 03:13
willsort兄的代码的确精简也很精彩,但是有个问题。如果要获取某个盘下所有子目录的文件则有问题,结果输出只有盘符。
作者: zh159     时间: 2006-12-10 14:29
把俺最新的也加入这贴吧^_^
@echo off
for /f "delims=" %%i in ('dir/a-d/b *.mp3') do call set $%%random%%$%%i=$
for /f "tokens=1,2* delims=$=" %%i in ('set $') do echo %%j
pause

作者: 3742668     时间: 2006-12-10 14:57
利用sort /+n 也能一定程度上的随即列表。
@echo off
    set /a num=%random% %% 4 + 1
    dir /b /a-d | sort /+%num%
pause
exit /b 0
它要求文件名相似度不能太高。
作者: qzwqzw     时间: 2006-12-10 16:05
可以考虑使用不同的%num%反复sort来增大随机度。
作者: 3742668     时间: 2006-12-11 07:49
反复sort也只是以最后一个为准,如果sort一次提取一次的话无疑是舍本逐末。
如果要求不高,可以浅尝辄止;否则还是利用 random 来得好。
作者: mqi     时间: 2006-12-20 23:36
请看看这样行不行啊
@echo off
for  %%i in (*.mp3) do (
                   echo %%i >>1.txt
)
exit
作者: gocndos     时间: 2007-1-21 01:33
收下了,以后研究
作者: hngaoshou     时间: 2007-2-1 02:58
再顶
实在是太好了
作者: jmzsyt     时间: 2007-3-2 02:28
以实现WMP的随机播放.
播放器里就有这个功能吧!这么多改来改去的多麻烦
作者: nzisisco     时间: 2007-3-9 02:28
全是高手 i 服了 u
作者: test266     时间: 2007-3-9 10:15    标题: 试试我的

@echo off
setlocal ENABLEDELAYEDEXPANSION

set /p mypath=请输入MP3文件所在的目录,直接回车使用当前目录:
set t1=%time%
set /a num=1
rem 下面这行不能处理有空格的路径(谁能告诉我怎么解决呀)
rem for /f %%i in ('dir /b *.mp3') do (
rem 下面这行可以处理空格,请自己修改路径
for /R %mypath% %%i in (*.mp3) do (
set s!num!=%%i
set /a num+=1
)

set /a fcount=%num%
set /a fcount-=1

:loop
set /a rnd=%random%
set /a rnd%%=%num%
set /a rnd+=1

if not "!s%rnd%!"=="" (
  echo !s%rnd%!
  call :swap %rnd% "!s%num%!"
  set s%num%=
  set /a num-=1
)

if %num% GTR 1 goto loop

echo 文件总数:%fcount%
echo 开始时间:%t1%
echo 结束时间:%time%

:swap
set stmp=%2
set s%1=%stmp:~1,-1%

[ Last edited by test266 on 2007-3-9 at 11:29 AM ]
作者: jmz573515     时间: 2007-3-9 22:47
楼下的好像不能处理带有空格和减号的文件名...
作者: linee     时间: 2008-12-30 23:27


  Quote:
Originally posted by zh159 at 2006-12-10 14:29:
把俺最新的也加入这贴吧^_^

@echo off
for /f "delims=" %%i in ('dir/a-d/b *.mp3') do call set $%%random%%$%%i=$
for /f "tokens=1,2* delims=$=" %%i in ('set $ ...

把这个改来听歌不错,空列表随机播放。
@echo off
for /f "delims=" %%i in ('dir/a-d/b/s *.mp3,*.wma,*.ape') do call set $%%random%%$%%i=$
for /f "tokens=1,2* delims=$=" %%i in ('set $') do "C:\Program Files\MPlayer\mplayer.exe" "%%j"
感觉下面这个要稍快点。
@echo off
for /r . %%i in (*.mp3,*.wma,*.ape) do call set $%%random%%$%%i=$
for /f "tokens=1,2* delims=$=" %%i in ('set $') do "C:\Program Files\MPlayer\mplayer.exe" "%%j"
[ Last edited by linee on 2008-12-31 at 05:28 ]
作者: linee     时间: 2009-1-5 15:07
参考24楼,34楼,把我修改的也贴在这吧。
@echo off
(for %1 %%i in (*.mp3,*.wma)do call echo %%random%%=%%i)%3>nul&%2:EOF
for /f "tokens=2delims==" %%a in ('"%~0" /r,goto,2^|sort')do echo %%a

@echo off&%~1
for /f "tokens=2delims==" %%a in ('%~0 "(for /r %%i in (*.mp3,*.wma)do call echo %%^random%%=%%i)&goto:EOF"^|sort')do echo %%a
[ Last edited by linee on 2009-1-7 at 23:09 ]