[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
返回列表 发帖
本帖最后由 zm900612 于 2011-5-16 13:28 编辑

来一个以sort为核心的全新思路:
  1. @echo off&setlocal enabledelayedexpansion
  2. set /a $=11,n=4096
  3. for /l %%a in (-2 1) do set $=!$!!$!!$!!$!
  4. (copy a.txt %$%$&findstr .* %$%?>$
  5. for %%a in (2048 1024 512 256 128 64 32 16 8 4 2 1) do sort /+100 /rec !n! $&&set /a n-=%%a,h=n-130||set /a n+=%%a)>nul 2>nul
  6. for /f "delims=" %%a in ('sort /+%h% a.txt') do set long=%%a
  7. echo !long!
  8. del /f /q *$&pause
复制代码
看到这题又浮了上来,想起正好能用上前阵子开发的用sort命令判断文件中最长行字符数的代码,于是把代码改造了下...
这种以外部命令为主的算法计算小文件时不快,但是在计算大文件时将有速度优势。

TOP

本帖最后由 zm900612 于 2011-5-16 13:37 编辑
23# zm900612
代码过于求奇求异
又缺乏必要的注释
是在考验读者的耐心
我不欣赏这样的风格
qzwqzw 发表于 2011-5-15 21:21

求奇求异倒不是刻意为之,只是平时喜欢发掘命令的新鲜用法,积累多了,在特定的场合自然会联想到,如果别人不了解我的积累,肯定不容易一下就看明白思路是什么...至于注释,确实没有养成这个代码习惯,以后会注意。

附上对23楼代码的解读:
首先要先说明一下,核心代码来自于前不久研究sort的rec开关时想到的用法:
  1. @echo off&setlocal enabledelayedexpansion
  2. set n=32768
  3. (for %%a in (16384 8192 4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do sort /+100 /rec !n! 1.txt&&set /a n-=%%a||set /a n+=%%a)>nul 2>nul
  4. ::很像plp兄曾转发过的折半回溯法求字符长度的函数吧?是的,这里就是对那个思路的改造,只不过我是利用sort /rec超过最长行字数时会出错的特性来进行运算的。
  5. echo 最长行有%n%个字符
  6. pause
复制代码
但是当时这个代码有个缺点,就是当最长行的字数低于128时,sort /rec的值无论设为多少都是不会出错的。
好在曾经拜读过用findstr实现多色显示的代码,从中获取了灵感,于是23楼代码中就用同样的原理为a.txt中每行补齐128个字符的前缀,这样就绕过了原先的限制
23楼代码为了简化而写得比较晦涩,现在翻译成大众版:
  1. @echo off&setlocal enabledelayedexpansion
  2. set /a tmp=11,n=4096
  3. ::此处的tmp是用于补位的,设成任意两个字符都行,为了压缩代码,把它设成十位数
  4. for /l %%a in (1 1 3) do set tmp=!tmp!!tmp!!tmp!!tmp!
  5. ::将tmp补足128位
  6. copy>nul a.txt %tmp:~2%$
  7. ::将a.txt保存到一个文件名长度为127个字符的临时文件中
  8. findstr .* %tmp:~2%?>$
  9. ::用findstr命令配合通配符实现补位,127个字符长的文件名加上“:”号,刚好128个字符,将结果输出到临时文件中
  10. for %%a in (2048 1024 512 256 128 64 32 16 8 4 2 1) do (
  11.    sort /+100 /rec !n! $&&set /a n-=%%a,h=n-128||set /a n+=%%a
  12. )>nul 2>nul
  13. ::用sort /rec判断该临时文件最长行的长度
  14. for /f "delims=" %%a in ('sort /+%h% a.txt') do set long=%%a
  15. ::因为知道了最长行的长度,所以可以直接用sort /+n来精确排序,通过for命令,很容易获得最后一行的内容(用set /p也可以,但是考虑到变量长度,还是用for保险,当然用findstr取指定行也是一个不错的选择)。
  16. echo !long!
  17. ::不解释
  18. del /f /q *$
  19. ::删除临时文件,销赃
  20. pause
复制代码
1

评分人数

TOP

23楼的代码中,至少用了三种比较偏门的技巧,所以看起来就显得奇异一些...

TOP

代码看着顺畅多了
不需要实际测试与断点跟踪也能明白思路
给两个建议:
1、set /a h=n-128移到for之外
2、取最长行用for/f+sort又成为线性算法
for/f需要完整遍历整个文件才能取得最长行
与你通篇的算法思路相 ...
qzwqzw 发表于 2011-5-16 15:28


1、这个,如果没记错的话,一个set /a的用时好像是set /a内部二十几个算式的计算耗时,而此处实际上只需要计算12次,所以我感觉这个算式还是能联用就联用
2、最初确实忽略了思路的连贯性,刚刚解释代码的时候也想到这一点,所以加了句用“findstr也是不错的选择”
关于变量长度,set /p与set的区别在于set "str=#@#¥#……&"时变量长度上限8192字节,而set /p str=请输入#@#¥#……&时则只能定义1024字节,这与前面折半回溯所支持的字符长度相悖,所以放弃了set /p。
改进的思路是:
  1. endlocal
  2. for /f "tokens=1* delims=:" %%a in ('findstr /n a.txt^|findstr 1:') do set "long=%%b"
  3. ...
复制代码
3、没看到楼主要求是不含空格,不过好像在谁的代码中看到了"findstr /o"...
我的代码习惯可能比较明显,要么大量使用外部命令,要么一堆for嵌套+set,减少call和goto。如果碰到大文件,外部命令优势明显,但是若要进行更精细的筛选,那还是只好老老实实用for了...

TOP

本帖最后由 zm900612 于 2011-5-16 21:16 编辑
set /a的问题不是在效率上
而是在逻辑上
因为你在set /a n+=%%a时并没有计算h
那么如果set /a n-=%%a在特定条件下没有运行一次
那么h的取值为空
或者set /a n-=%%a最后一次没有运行
那么h的取值会差1

set/p ...
qzwqzw 发表于 2011-5-16 20:11

这个确实没想到,没顾虑到特殊情况。

关于改进思路,汗一个,错了不止一处,应该改成:
  1. endlocal
  2. for /f "tokens=1* delims=:" %%a in ('sort /r /+%h% a.txt^|findstr /n .*^|findstr /b 1:') do set "long=%%b"
  3. ...
复制代码

TOP

32# qzwqzw


又忘了,已修改

TOP

在循环中非常高效的表驱动法:
  1. @echo off&setlocal enabledelayedexpansion
  2. set "tmp= 0 9 8 7 6 5 4 3 2 1"
  3. for /l %%a in (0 1 4) do set var=!tmp: =%%a!!var!
  4. for /f "delims=" %%a in (a.txt) do (
  5. set str=%%a
  6. set m=!str: =!!str: =!!var!
  7. if "1!m:~100,2!" gtr "1!max!" (
  8. set max=!m:~100,2!
  9. set text=%%a
  10. )
  11. )
  12. echo !text!
  13. pause
复制代码
同样的算法稍加改动,日后最多可以兼容四千多位:
  1. @echo off&setlocal enabledelayedexpansion
  2. for /l %%a in (1 1 5) do set a=!a!0987654321
  3. for /l %%a in (0 1 4) do set b=%%a%%a%%a%%a%%a%%a%%a%%a%%a%%a!b!
  4. for /f "delims=" %%a in (a.txt) do (
  5. set str=%%a
  6. set m=!str: =!!a!
  7. set n=!str: =!!b!
  8. if "1!n:~50,1!!m:~50,1!" gtr "1!max!" (
  9. set max=!n:~50,1!!m:~50,1!
  10. set text=%%a
  11. )
  12. )
  13. echo !text!
  14. pause
复制代码

TOP

返回列表