Board logo

标题: [文件操作] [已解决]批处理如何快速统计某目录下文件数? [打印本页]

作者: zhouyongjun    时间: 2010-4-7 10:31     标题: [已解决]批处理如何快速统计某目录下文件数?

本人工作需要写了个批处理,其中一段需要获取指定目录下文件的数量,写来写去总是存在效率问题,特发帖集思广益,求高效之法,问题简化如下(可用第三方):
文件夹ABC中可能存在8000-10000个左右的文件,为每隔一定时间软件自动拷贝而来,文件格式为“A_B.txt”,其中A、B均为数字,A不固定,B代表区域。(B≤9999)
文件列表示例如下:
1_1.TXT
1_2.TXT
1_3.TXT
2_4.TXT
2_2.TXT
......
1_1.TXT代表1区域的数据,1_2.TXT、2_2.TXT代表2区域的数据
现需统计文件所代表区域总数,即总共已拷贝多少区域?
(另一种表述:上示列表红色数字不重复的总个数是多少?)

PS:我采用的for /r、for /f+dir速度都不理想
  1. @echo off
  2. set t=%time%
  3. for /f "tokens=2 delims=_." %%i in ('dir /b abc\*.txt') do (
  4.     if not defined @@%%i set /a upn+=1
  5.     set /a @@%%i=0
  6. )
  7. echo %t%
  8. echo %time%
复制代码
  1. @echo off
  2. set t=%time%
  3. for /r "abc" %%i in (*.txt) do (
  4.     for /f "tokens=2 delims=_" %%a in ("%%~ni") do (
  5.         if not defined @@%%a set /a upn+=1
  6.         set /a @@%%a=0
  7.     )
  8. )
  9. echo %t%
  10. echo %time%
复制代码
-------------------------------------------------------------------------------------------
问题延伸
◇此模块主要实现功能为求出剩余区域及数量
◇剩余区域需要先求出,当剩余区域大于某值时将不详细显示具体剩余区域
◇配置文件中有定义区域范围,如“1-9999”
◇根据指定文件夹(例如ABC文件夹)中已有文件求出剩余区域

[ 本帖最后由 zhouyongjun 于 2010-4-7 16:28 编辑 ]
作者: caruko    时间: 2010-4-7 11:37

BAT还有比DIR 更快的遍历方式吗?
而且不管怎么遍历,都要FOR一次..
既然如此,速度要提高就很难了
作者: caruko    时间: 2010-4-7 11:43

sed不熟,或者可以快速求出...
作者: zhouyongjun    时间: 2010-4-7 11:49     标题: 回复 3楼 的帖子

第三方也行,因为是工作需要,不要考虑太多可移植性,速度第一位
作者: GNU    时间: 2010-4-7 12:52

  1. dir /b *_*.txt | gawk -F"[_.]" "{a[$2]++}END{for(i in a)print i,a[i]}"
复制代码

作者: namejm    时间: 2010-4-7 12:55

  通过反复测试,发现顶楼代码速度的瓶颈在于频繁的 set /a 运算,如果换成 set 的赋值语句而不是set的数值运算语句,速度将大为提升。

  以下代码应当可以提高一点速度,但是会带来两个副作用:1、CPU占用飙升,将会达到100%;2、使用了临时文件:
  1. @echo off
  2. for /f "tokens=2 delims=_." %%i in ('dir /b abc\*.txt') do (
  3.     set ID_%%i=0
  4. )
  5. set ID_>tmp
  6. for /f "delims=:" %%i in ('findstr /n . tmp') do set num=%%i
  7. echo %num%
  8. echo %t%
  9. echo %time%
  10. pause
复制代码
  由于通过重定向符号>把内容倒入文本中后,会在最后一行形成仅含回车符号的空行,无法通过 findstr /n  /v "$" tmp 语句来快速获取最后一行的行号,只能逐行编号,通过set语句把最后一行的行号赋予num,使得效率大为减慢。
作者: neorobin    时间: 2010-4-7 13:52

在 6 楼代码的基础上改了下, 加了一个 ECHO.>>tmp 使 'findstr /n /v "Y$" tmp' 可以有结果
  1. @echo off
  2. set t=%time%
  3. for /f "tokens=2 delims=_." %%i in ('dir /b abc\*.txt') do set ID_%%i=Y
  4. set ID_>tmp
  5. ECHO.>>tmp
  6. for /f "delims=:" %%i in ('findstr /n /v "Y$" tmp') do set /a sum=%%i-1 & set sum
  7. echo %t%
  8. echo %time%
  9. pause
复制代码

作者: zhouyongjun    时间: 2010-4-7 14:49

感谢楼上几位
原来set /a 的赋值效率竟没有直接set高。
测试文件8000个,我顶楼第一个代码17S,第二个21S,第一个set /a 改为set 15S
JM的代码为12S,neorobin的代码有了很大的提速,将近5S
最快的是GUN兄的,但似乎只求出的文件数,并没考虑一个区域存在两个文件或多个
搜索了gawk,发现GUN竟是原创作者,先敬仰一下。
但是未曾发现gawk的帮助内容,不知GUN兄可否再帮忙一下。
作者: zhouyongjun    时间: 2010-4-7 16:25

找了一下资料gawk真是太厉害了
GNU的代码这样改一下就可以达到我要求了
  1. dir /b abc\*.txt | gawk -F"[_.]" "{if (a[$2] != "1") a[$2]++ s++}END{print s}"
复制代码

[ 本帖最后由 zhouyongjun 于 2010-4-7 16:50 编辑 ]
作者: zhouyongjun    时间: 2010-4-7 16:28

不用第三方,7楼是目前的最优解
再次感谢各位
结贴
作者: namejm    时间: 2010-4-7 16:52

  7楼的代码在原有文本末尾强制添加换行符号,构造出一个带回车换行的空行,并巧妙地利用了findstr的特点,一举节约了大量时间,妙哉。
  本帖解决问题的过程堪称经典,十分具有借鉴意义,高亮之。
作者: zhouyongjun    时间: 2010-4-7 17:05

虽然结贴,然9楼有一点问题仍需要提出,采用gawk之法不能开启变量延迟
发现开启变量延迟结果变成文件数
具体原因不清楚
作者: Batcher    时间: 2010-4-7 19:37     标题: 回复 8楼 的帖子

1、GNU是一个组织
2、awk文章收集
http://bbs.bathome.net/thread-3997-1-1.html
作者: FOR    时间: 2010-4-7 20:39

这样呢?
  1. @echo off
  2. for /f "tokens=2 delims=_." %%i in ('dir /b abc\*.txt') do set ID_%%i=0
  3. set ID_>tmp
  4. for /f "tokens=3 delims= " %%I in ('find /c /v "" tem') do echo %%I
  5. pause
复制代码

作者: namejm    时间: 2010-4-7 20:59

  哈哈,有更巧妙的方法出现了,更加有效率——不过后一句的文件名写错了,应该是tmp。
作者: zhouyongjun    时间: 2010-4-8 09:42     标题: 回复 13楼 的帖子

谢谢
我就是阅读了那个贴才有我9楼的修改,gawk之强值得学习
作者: zhouyongjun    时间: 2010-4-8 09:44

经测试14楼的效率比7楼的略低,难道是findstr和find查询效率的原因?
作者: hanyeguxing    时间: 2010-4-8 11:42

  1. @echo off
  2. for /f "tokens=2 delims=_." %%i in ('dir /b abc\*.txt') do set ID_%%i=0
  3. for /f %%I in ('set ID_') DO set/a n+=1
  4. echo %n%&pause
复制代码

作者: namejm    时间: 2010-4-8 15:47

  虽然楼上的代码避免了使用临时文件,但是,和其他使用了临时文件的代码相比,效率是很低下的,原因我在前面已经提过,那就是频繁地使用了 set /a 语句。
作者: hanyeguxing    时间: 2010-4-8 17:10

  1. @echo off
  2. for /f "tokens=2 delims=_." %%i in ('dir /b abc\*.txt') do set ID_%%i=0
  3. (for /f "delims=" %%i in ('set ID_') do echo.)>tmp
  4. for %%i in (tmp) do set/a n=%%~zi/2
  5. echo.%n%
  6. pause
复制代码
1万个ID_测试,比14楼的依然慢了0.28秒,惭愧一个。。。

[ 本帖最后由 hanyeguxing 于 2010-4-8 18:43 编辑 ]
作者: hanyeguxing    时间: 2010-4-8 18:24

原帖由 FOR 于 2010-4-7 20:39 发表
这样呢?@echo off
for /f "tokens=2 delims=_." %%i in ('dir /b abc\*.txt') do set ID_%%i=0
set ID_>tmp
for /f "tokens=3 delims= " %%I in ('find /c /v "" tem') do echo %%I
pause


抄袭14楼的,然后改了下:
  1. @echo off
  2. for /f "tokens=2 delims=_." %%i in ('dir /b abc\*.txt') do set ID_%%i=0
  3. set ID_|find /c "0"
  4. pause
复制代码
呵呵,1万个ID_测试,快了0.1秒

[ 本帖最后由 hanyeguxing 于 2010-4-8 18:42 编辑 ]
作者: x9tiancmd    时间: 2010-4-9 21:01

原帖由 hanyeguxing 于 2010-4-8 18:24 发表


抄袭14楼的,然后改了下:@echo off
for /f "tokens=2 delims=_." %%i in ('dir /b abc\*.txt') do set ID_%%i=0
set ID_|find /c "0"
pause呵呵,1万个ID_测试,快了0.1秒


按题目新建 文件md abc>nul&pushd abc& for /l %%i in (1,1,1000) do cd.>1_%%i.txt
试了下:

find /c "0"   改成 14 楼的 find /c /v ""
还要快些,  呵呵, 真是长见识了

[ 本帖最后由 x9tiancmd 于 2010-4-9 21:07 编辑 ]




欢迎光临 批处理之家 (http://www.bathome.net/) Powered by Discuz! 7.2