返回列表 发帖
如果文本行都在80个字符以内的话,可用以下这种高效的办法:
@echo off&setlocal enabledelayedexpansion
for /l %%a in (1,1,80) do set "k=!k!0"
for /f "delims=" %%a in (a.txt) do (
    set "str=%%a"&set "str=%k% !str: =!"
    set "str=!str:~-80!"&set /a n+=1
    for /f %%b in ("!str!") do set "_%%b=%%a"&set ".%%a=!n!"
)
for /f "tokens=1,2 delims==_" %%a in ('set _') do (
    set "str=%%a"&set "var=%%b"
    for /l %%i in (1,1,80) do if "!str:~%%i!" equ "" set /a num=79-%%i&goto next
)
:next
echo 字符数是多的是!.%var%!行,共有%num%个字符。
pause>nulCOPY
***共同提高***

TOP

折半法 可处理每行最多 8189 个字符,效率不错。
@echo off&setlocal enabledelayedexpansion
::@随风 @bbs.bathome.net @2009-04-18
for /f "delims=" %%a in (a.txt) do (
   set /a sun+=1,n=8189*2,max=1
   set s=%%a&set s=!s: =!&set "var="
   for /l %%a in (1 1 14) do (
      if defined var set /a n=var
      set /a n/=2
      for %%i in (!n!) do (
         if "!s:~%%i,1!"=="" (set /a var=n) else (
            set s=!s:~%%i!&set /a max+=%%i,var-=%%i
   )))  
   if !h! lss !max! set /a num=sun,h=max
)
echo 字符数最多的行为:!num!
echo 字符数为:!h!
pauseCOPY

[ 本帖最后由 随风 于 2009-4-18 17:01 编辑 ]
技术问题请到论坛发帖求助!

TOP

取长补短为提速

@echo off&setlocal enabledelayedexpansion
set m=0
for /f "delims=" %%a in (a.txt) do (
    set "str=%%a"&set "str=!str: =!0"
    for %%b in (!m!) do (if "!str:~%%b,1!" neq "" call :sub "%%a")
)
echo 字符数是多的是%var%行,共有%m%个字符。
pause
goto :eof
:sub
set/a en=m+40
for /l %%a in (!m!,1,!en!) do (
if "!str:~%%a,1!" equ "" (
  set/a n=%%a-1
  if !m! lss !n! set/a m=!n!,n=0&set "var=%~1"
  goto :eof
)
)
set/a m=en
goto :subCOPY

TOP

请留意中间的输出,此法应该是最高效了吧?
@echo off&setlocal enabledelayedexpansion
set m=1
for /f "delims=" %%a in (a.txt) do (
    set "str=%%a"&set "str=!str: =!0"
    for %%b in (!m!) do (if "!str:~%%b,1!" neq "" call :sub "%%a")
)
set /a m-=1
echo 字符数是多的是%var%行,共有%m%个字符。
pause
goto :eof
:sub
set/a en=m+40
for /l %%a in (!m!,1,!en!) do (
        if "!str:~%%a,1!" equ "" (
                echo 较多: %~1
        set/a m=%%a&set "var=%~1"&goto :eof)
)
set/a m=en
goto :subCOPY
1

评分人数

TOP

19楼思路非常巧妙,从少到多,只判断字符数多的行,不判断少的行。省略了很多无用的判断。
技术问题请到论坛发帖求助!

TOP

看来从心动到行动
还有很长的距离啊
::  GetMaxCharText.cmd - 找出字符最多的文本行
::  qzwqzw - 2010-01-05
::
::  基本构思:
::      将原文本每行尾部加足够的一定数量的空格,在文本右侧形成一个不等齐空格区域
::      原文本行越长,其后的空格区域就越长,然后用sort /+n 仅对空格区域进行排序
::      因为区域内都是空格,则排序的依据就是空格的长度,也就是原行文本的长度
::
@echo off & setlocal EnableDelayedExpansion
set maxLineLen=80
set infile=%~sf0
for /l %%i in (1,1,%maxLineLen%) do set zone= !zone!
:: findstr 会忽略空行和仅含空格的行,但显示行号会跨过这些行,这是我们需要的特性
:: 将行号与行文本分别处理,是因为不对齐行号会影响行文本长度的判断
(for /f "tokens=1* delims=:" %%l in ('findstr /n /r /c:"[^ ]"  %infile%') do (
    set LineNo=         %%l
    set LineNo=!LineNo:~-10!
    set Line=%%m
    set Line=!Line: =!
    echo.!LineNo!:!Line!!zone!
))>%temp%\%~sn0~1.t~1
sort /r /+%maxLineLen% %temp%\%~sn0.t~1 > %temp%\%~sn0.t~2
set/p maxLine=< %temp%\%~sn0.t~2 >nul
::  已找到最多字符的行,以下是提取行号和原行文本,行长度因未要求而忽略
for /f "delims=: " %%i in ("%maxline%") do set maxLineIdx=%%i
::  使用find而不是findstr,是因为find够用够简单
set seekLine=find /v /n "" ^^^<%infile%^^^|find "[%maxLineIdx%]"
echo.字符最多的行第 %maxLineIdx% 行,内容为:
for /f "tokens=1* delims=[]" %%l in ('%seekLine%') do echo.%%m
for %%f in (%temp%\%~sn0.t*) do del %%fCOPY

[ 本帖最后由 qzwqzw 于 2010-1-5 10:46 编辑 ]

TOP

@echo off&setlocal enabledelayedexpansion
for /f "delims=" %%a in (a.txt) do (
     set /a t+=1
     set i=%%a
     set y!t!=!i!
     set i=!i: =!
     call:x !i!
     set /a n!t!=!num!
)
for /l %%b in (1 1 !t!) do (
if !n%%b! lss !number! (
set /a n%%b=number
set y%%b=!str!
)
set /a number=n%%b
set str=!y%%b!
cls
echo 字符数最多的行是:!y%%b!共有字符:!n%%b! 个
)
pause>nul&exit
:x
    set i=%1
    set num=
:loop
     set i=!i:~1!
     set /a num+=1
     if "!i!"==""  ( echo abc >nul
     )  else ( goto:loop)COPY

[ 本帖最后由 vincentzhou 于 2011-1-9 13:27 编辑 ]
only the strong survive!

TOP

本帖最后由 zm900612 于 2011-5-16 13:28 编辑

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

TOP

23# zm900612
代码过于求奇求异
又缺乏必要的注释
是在考验读者的耐心
我不欣赏这样的风格
天的白色影子

TOP

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

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

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

评分人数

TOP

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

TOP

代码看着顺畅多了
不需要实际测试与断点跟踪也能明白思路
给两个建议:
1、set /a h=n-128移到for之外
2、取最长行用for/f+sort又成为线性算法
for/f需要完整遍历整个文件才能取得最长行
与你通篇的算法思路相左
不如仍然用set/p+sort /r
至于变量长度这与for/f中的set long有不同吗?
天的白色影子

TOP

另外发现一个小问题
悄悄的告诉你
楼主的题目要求是获取不包含空格的字符数最多行
天的白色影子

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。
改进的思路是:
endlocal
for /f "tokens=1* delims=:" %%a in ('findstr /n a.txt^|findstr 1:') do set "long=%%b"
...COPY
3、没看到楼主要求是不含空格,不过好像在谁的代码中看到了"findstr /o"...
我的代码习惯可能比较明显,要么大量使用外部命令,要么一堆for嵌套+set,减少call和goto。如果碰到大文件,外部命令优势明显,但是若要进行更精细的筛选,那还是只好老老实实用for了...

TOP

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

set/p的字符长度限制确实是忘记了
记忆力确实越来越差了
扩展测试了一下
windows记事本也有每行1024字符的限制
find命令同样也有

不过你的改进思路有些问题
似乎忘记了sort /r
天的白色影子

TOP

返回列表