中国DOS联盟

-- 联合DOS 推动DOS 发展DOS --

联盟域名:www.cn-dos.net 论坛域名:www.cn-dos.net/forum
DOS,代表着自由开放与发展,我们努力起来,学习FreeDOS和Linux的自由开放与GNU精神,共同创造和发展美好的自由与GNU GPL世界吧!

中国DOS联盟论坛
现在时间是 2026-07-04 18:05
中国DOS联盟论坛 » DOS批处理 & 脚本技术(批处理室) » 统计文本文件的字符个数 查看 3,579 回复 31
16 计算setlocal最大递归层(为32层): 发表于 2008-04-25 22:11 ·  中国 湖南 株洲 电信
金牌会员
★★★★
永远的学习者
积分 3,105
发帖 1,276
注册 2008-03-08 13:00
18年会员
UID 112398
性别 男
状态 离线
可能有人会有这样的疑问: 不执行endlocal行不行。。
答案是也行, 就比如第二段代码

但是要记住: 一定要在 set "var=%%a"之前关闭掉 变量延迟

这样会不断的递归产生 环境表。

对于小文件还尚可。。 文件行数稍大便会 导致setlocal递归层数过大

@echo off
for /l %%i in (1,1,33) do (
set str=%%i
setlocal enabledelayedexpansion
echo !str!
)
pause>nul

在显示了32后就会提示:setlocal已到最大递归层,而在echo !str!下面加上endlocal就不会有这个提示了。

[ Last edited by zw19750516 on 2008-4-25 at 10:14 PM ]
批处理之家新域名:www.bathome.net
17 发表于 2008-04-25 22:24 ·  中国 浙江 杭州 电信
银牌会员
★★★
积分 2,000
发帖 621
注册 2007-01-01 00:00
19年会员
UID 75212
性别 男
状态 离线
受10楼兄弟的启发, 下面这个代码即可统计 不必产生临时文件,
而且也 发现了 把变量传递回来的好办法。 很聪明的一个办法。。。

@echo off
for /f "delims=" %%a in ('findstr /n .* a.txt') do (
set "var=%%a"
call :list
)

echo.%total%
pause>nul
goto :eof

:list
setlocal enabledelayedexpansion
set /a n=0
set var=!var:*:=!
call :loop
endlocal&&set /a total+=n
goto :eof

:loop
if defined var (
set /a n+=1
set var=!var:~1!
goto loop
)


主要在两点。。
1) 让 set /a total+=n 脱离 循环的区域 但又被执行到
原因是为了避免 在for中 没执行前的命令行扫描 即将 n 替换掉
2)endlocal&&set /a total+=n
很聪明的做法 也是传递回来的根本原因所在
因为在一行。。。 所以 命令行 会对整体进行扫描
因为扫描时还没有执行endlocal, n 的值仍然存在, 而对其的替换恰发生在此时
扫描完毕后 真正执行时,endlocal 后 还原回原来的环境变量表, 但是
set /a total+=n 在扫描后 n 被替换为一个确定的值, 因此n的值通过total
被传递回原来的环境表量表

刚好利用了 扫描替换变量和执行的时间差。。

注意一定要在同一行,
endlocal
set /a total+=n
分开写, 第二行则永远是零了。。

[ Last edited by bjsh on 2008-4-26 at 11:27 AM ]
18 发表于 2008-04-25 22:37 ·  中国 陕西 西安 电信
银牌会员
★★★★
钻石会员
积分 2,278
发帖 1,020
注册 2007-11-19 13:34
18年会员
UID 103127
性别 男
状态 离线
2)endlocal&&set /a total+=n

这个是函数常用的格式:

有参函数:

call:标签A "参数1" "参数2"


rem 注释....(该函数的功能等)

:标签A
setlocal ENABLEEXTENSIONS

参数接收
任务A

endlocal&设定返回参数1,参数2,...
goto :eof


详细这个帖子:

http://www.cn-dos.net/forum/viewthread.php?tid=38969&fpage=1

既然版主都来了,相比能给这个帖子提些建设性的意见.....
我在做这方面的工作,一个人太累,
山外有山,人外有人;低调做人,努力做事。

进入网盘(各种工具)~~ 空间~~cmd学习
19 发表于 2008-04-25 22:58 ·  中国 山东 淄博 联通
银牌会员
★★★
积分 1,604
发帖 646
注册 2008-04-13 23:39
18年会员
UID 115804
性别 男
状态 离线
Originally posted by bjsh at 2008-4-25 10:24 PM:
受10楼兄弟的启发, 下面这个代码即可统计 不必产生临时文件,
而且也 发现了 把变量传递回来的好办法。 很聪明的一个办法。。。

@echo off
...


17楼版主的代码测试有误
a.txt:
abc!d!e^~
cdk!%!#$56%!Q!
计算出来是26个,为何会多了一个~
20 发表于 2008-04-25 23:03 ·  中国 湖南 娄底 电信
银牌会员
★★★
积分 2,268
发帖 879
注册 2006-12-19 16:23
19年会员
UID 73968
性别 男
状态 离线
回复 17 楼
最开始也是采用这种方法,但实际上是不行的,
不信把a.txt内容超过32行,同样会提示 以达到最大递归层,
并且在末位 echo !total! 同样有结果,说明 延迟变量还是开启的.
在返回call主程序之前endlocal 好像并不能结束延迟变量
endlocal&set /a w+=n 这种方法好像在for中无效,也就是在()中无效,所以在10楼,先跳出for即可.

[ Last edited by 26933062 on 2008-4-25 at 11:09 PM ]
致精致简!
21 发表于 2008-04-25 23:47 ·  中国 广东 广州 越秀区 电信
初级用户
积分 38
发帖 10
注册 2007-10-01 13:25
18年会员
UID 98696
性别 男
来自 GZ
状态 离线
@echo off&setlocal enabledelayedexpansion
echo 统计中...
for /f "delims=" %%i in (a.txt) do call :split "%%i"
cls&echo 共有!count!个字符。
pause>nul&exit
:split
set c=-1&set "s=%~1"
:next
set /a c+=1
if not "!s:~%c%!" == "" (
set /a count+=1
goto next
)
沉浸于脚本的流浪客。
纳须弥于芥子,容世界之脚本...
22 发表于 2008-04-26 00:09 ·  中国 浙江 杭州 电信
银牌会员
★★★
积分 2,000
发帖 621
注册 2007-01-01 00:00
19年会员
UID 75212
性别 男
状态 离线
Originally posted by 26933062 at 2008-4-25 11:03 PM:
回复 17 楼
最开始也是采用这种方法,但实际上是不行的,
不信把a.txt内容超过32行,同样会提示 以达到最大递归层,
并且在末位 echo !total! 同样有结果 ...


确实。。。
setlocal 和 endlocal 必须得在一个 块 内。。


还真得就这么写了。。


@echo off
for /f "delims=" %%a in ('findstr /n .* a.txt') do (
set "var=%%a"
call :list
)

echo.%total%
pause>nul
goto :eof

:list
setlocal enabledelayedexpansion
set /a n=0
set var=!var:*:=!
call :loop
endlocal&set /a total+=%n%
goto :eof

:loop
if defined var (
set /a n+=1
set var=!var:~1!
goto loop
)



回复 19楼。。
出错 也是 因为这个原因了。。 更改了源代码了。。

[ Last edited by bjsh on 2008-4-26 at 11:25 AM ]
本帖最近评分记录 (共 1 条) 点击查看详情
评分人分数时间
pusofalse +2 2008-04-26 00:17
23 发表于 2008-04-26 00:15 ·  中国 浙江 杭州 电信
银牌会员
★★★
积分 2,000
发帖 621
注册 2007-01-01 00:00
19年会员
UID 75212
性别 男
状态 离线
Originally posted by plp626 at 2008-4-25 10:37 PM:

这个是函数常用的格式:

有参函数:

call:标签A "参数1" "参数2"


rem 注释....(该函数的功能等)

:标签A
setl ...


恩。 好。 你的那个帖子 相当不错。
24 我来说两句,不对请指正: 发表于 2008-04-26 10:26 ·  中国 湖南 株洲 电信
金牌会员
★★★★
永远的学习者
积分 3,105
发帖 1,276
注册 2008-03-08 13:00
18年会员
UID 112398
性别 男
状态 离线
&&我们知道要原样输出文本如a.txt内容最好的方法就是使用type:
@type a.txt&pause>nul
那么bjsh版主在8楼总结出的代码,是不是可以把findstr改为type,一来可以提高代码效率,二来还可以简化代码(不足之处是会忽略以";"号打头的行):
@echo off
for /f "delims=" %%i in ('type a.txt') do set "str=%%i"&call :lp
echo 总字符数为%m%个
pause>nul&goto :eof
:lp
setlocal enabledelayedexpansion
:loop
if defined str (
set /a n+=1
set str=!str:~1!
 goto loop
)
endlocal&set /a m+=%n%

测试文本如下(用版主的测试提示找不到操作数):
(一)应用DOS重定向功能

DOS的标准输入输出通常是在标准设备键盘和显示器上进行的, 利用重定向,可以方便地将输入输出改向磁盘文件或其它设备。如在批处理命令执行期间为了禁止命令或程序执行后输出信息而扰乱屏幕, 可用DOS重定向功能把输出改向NUL设备(NUL不指向任何实际设备): C:\>COPY A.TXT B.TXT > NUL。

命令执行结束不显示"1 file(s) copied"的信息。有的交互程序在执行时要求很多键盘输入, 但有时输入是固定不变的, 为加快运行速度, 可预先建立一个输入文件,此文件的内容为程序的键盘输入项, 每个输入项占一行。假如有一个程序ZB, 其输入项全部包括在文件IN.DAT中, 执行 C:\>ZB NUL 程序就自动执行。

(二)应用DOS管道功能

DOS的管道功能是使一个程序或命令的标准输出用做另一个程序或命令的标准输入。如把DEBUG的输入命令写入文件AAA, 用TYPE命令通过管道功能将AAA的内容传输给DEBUG, 在DEBUG执行期间不再从控制台索取命令参数, 从而提高了机器效率。命令为: C:\>TYPE AAA|DEBUG >BBB。

(三)子程序

在一个批处理文件可用CALL命令调用另一个子批处理文件, 当子批文件执行结束后,自动返回父批文件, 继续向下执行。如: A.BAT B.BAT,A调用B,A.BAT内容如下:

@ECHO OFF
CALL B
CD \BASIC
BASICA BG
@ECHO ON

(四)菜单选择功能

DOS功能调用31H或4CH所提供的一字节的返回码, 通过批处理子命令IF和ERRORLEVEL对返回码进行处理, 可达到自动执行一批命令的目的。在批处理文件中实现高级语言所有的菜单提示功能, 使批处理文件变得更灵活方便。先用DEBUG建立一个菜单驱动程序MENU.COM,对应地编写一个批处理文件LG.BAT。具体内容和方法见下表:

DEBUG
-A
-166C:0100 MOV DX,111
-166C:0103 MOV AH,09
-166C:0105 INT 21
-166C:0107 MOV AH,01
-166C:0109 INT 21
-166C:010B MOV AH,4C
-166C:010D INT 21
-166C:010F INT 20
-166C:0111 DB '******************************'0D 0A
-166C:0131 DB '* 1.Turbo Pascal 5.00 *'0D 0A
-166C:0151 DB '* 2.Turbo Basci 1.00 *'0D 0A
-166C:0171 DB '* 3.Turbo Prolog 2.00 *'0D 0A
-166C:0191 DB '* 4.Turbo C 2.00 *'0D 0A
-166C:01B1 DB '* 0.Exit *'0D 0A
-166C:01B1 DB '******************************'0D 0A
-166C:01F1 DB 'Your choice(0..4) : '24 0D 0A 1A
-166C:0209
-R CX
CX 0000
:108
-N MENU.COM
-W
Writing 0108 bytes
-Q
@ECHO OFF:
START
CLS
MENU
IF ERRORLEVEL 52 GOTO C
IF ERRORLEVEL 51 GOTO PRO
IF ERRORLEVEL 50 GOTO BAS
IF ERRORLEVEL 49 GOTO PAS
IF ERRORLEVEL 48 GOTO EX
CLS
GOTO START
AS
CD \TP5.00
TURBO
CD \
GOTO START
:BAS
CD \TB
TB
CD \
GOTO START
RO
CD \TPROLOG
PROLOG
CD \
GOTO START
:C
CD \TURBOC
TC
CD \
GOTO START
:EX
@ECHO ON


执行LG, 屏幕左上角出现一个菜单, 并提示用户输入选择, 当选择的功能执行结束,重新返回主菜单请求选择, 直到选择"0"号功能, 程序结束返回DOS。

(五)应用命令处理程序完成大量重复工作

DOS提供调用次级命令程序的方法, 可实现与子程序等效的功能, 在MS DOS3.3以前的DOS版本下非常有用。如你有一批FORTRAN源程序需要编译, 首先编写两个批文件MAKEOBJ.BAT、C.BAT, 然后执行MAKEOBJ, 即可把当前目录下的所有扩展名为.FOR的FORTRAN源程序编译成OBJ文件。这种方法迅速正确, 人机交互少, 减轻了程序员的的大量劳动。

MAKEOBJ.BAT C.BAT
@ECHO OFF
ECHO COMPILE FORTRAN PROGRAMS.
FOR %%A IN (*.FOR) DO COMMAND /C C %%A
ECHO FINISH !
@ECHO ON @ECHO OFF
ECHO ------ COMPILE %1 ------
FOR1 %1; >NUL
FOR2 >NUL
@ECHO ON


[ Last edited by zw19750516 on 2008-4-26 at 11:20 AM ]
批处理之家新域名:www.bathome.net
25 发表于 2008-04-26 11:00 ·  中国 湖南 娄底 电信
银牌会员
★★★
积分 2,268
发帖 879
注册 2006-12-19 16:23
19年会员
UID 73968
性别 男
状态 离线
回 24 楼
for /f "delims=" %%i in ('type x.txt') do set  会忽略分号开头的行

对文档字符,字节进行详细统计,含特殊字符

@echo off
set /a k=0,qk=0,h=0,b=0
for /f "delims=" %%a in ('findstr /n .* a.txt') do set "var=%%a"&call :lis
set /a han+=quak,zong=ban+han*2
echo.&echo 字符总数 %u%
echo.&echo 字节总数 %zong%
echo.&echo 半角字符数量 %ban%
echo.&echo 半角空格数量 %kong%
echo.&echo 全角字符数量 %han%
echo.&echo 全角空格数量 %quak%
pause>nul
goto :eof
:lis
setlocal enabledelayedexpansion
set var=!var:*:=!
if not defined var endlocal&set /a w+=1&goto :eof
call :loop
endlocal&set /a u+=%n%,kong+=%k%,quak+=%qk%,han+=%h%,ban+=%b%
goto :eof
:loop
if defined var (
set /a n+=1
if "!var:~0,1!"==" " (set /a k+=1)
if "!var:~0,1!"==" " set /a qk+=1
if "!var:~0,1!" gtr "Z" (set /a h+=1) else set /a b+=1
set var=!var:~1!
goto loop
)
goto :eof

a.txt 内容

><
`123%456%7890-=\]














[ Last edited by 26933062 on 2008-4-26 at 11:21 AM ]
致精致简!
26 发表于 2008-04-26 11:14 ·  中国 湖南 株洲 电信
金牌会员
★★★★
永远的学习者
积分 3,105
发帖 1,276
注册 2008-03-08 13:00
18年会员
UID 112398
性别 男
状态 离线
Originally posted by 26933062 at 2008-4-26 11:00:
回 24 楼
for /f "delims=" %%i in ('type x.txt') do set  会忽略分号开头的行

对文档字符,字节进行详细统计,含特殊字符
:

@echo off
set /a ...

确实如此,用我的统计字符总数是428,实际是429,少统计了以分号开头的倒数第二行,是我忽略了这点,谢谢指正。

[ Last edited by zw19750516 on 2008-4-26 at 11:18 AM ]
批处理之家新域名:www.bathome.net
27 发表于 2008-04-26 11:25 ·  中国 浙江 杭州 电信
银牌会员
★★★
积分 2,000
发帖 621
注册 2007-01-01 00:00
19年会员
UID 75212
性别 男
状态 离线
Originally posted by zw19750516 at 2008-4-26 10:26 AM:
测试文本如下(用版主的测试提示找不到操作数):


呵呵

找不到操作数 是我忘了空行的情况

其实用 findstr 而不用type 当初也是因为这空行

空行时, 因为n没定义, 导致 endlocal 后的 set /a 报错 说 没有操作数:

setlocal enabledelayedexpansion
后加一句
set /a n=0
即可。

已修正。。
28 发表于 2008-04-26 11:32 ·  中国 浙江 杭州 电信
银牌会员
★★★
积分 2,000
发帖 621
注册 2007-01-01 00:00
19年会员
UID 75212
性别 男
状态 离线
效率么。。 确实很差。。

用批处理来写 就是写这么个算法。

不适合真正拿来用。。

字符处理 是 cmd 的一个瓶颈。。

最好还是用其他的方式来解决。。

用最适合的方式解决最适合的问题。。
29 发表于 2008-04-26 13:39 ·  中国 湖南 株洲 电信
金牌会员
★★★★
永远的学习者
积分 3,105
发帖 1,276
注册 2008-03-08 13:00
18年会员
UID 112398
性别 男
状态 离线
严重同意版主的意见!!!
批处理之家新域名:www.bathome.net
30 发表于 2010-01-10 12:34 ·  中国 江苏 苏州 电信
新手上路
积分 1
发帖 1
注册 2010-01-09 12:39
16年会员
UID 158297
性别 男
状态 离线
.
.
.
.



为什么要end local 啊 不关闭也可以执行啊。







@echo off& mode con cols=40 lines=10& setlocal enabledelayedexpansion& color 9f
(if "%~1"=="" (echo 把文件托到这里,然后按个回车:
set /p wind_F=)else set wind_F=%~1)& cls
for /f "tokens=1* delims=:" %%1 in ('findstr /n ".*" "%wind_F%"')do (set "wind=%%2"& set /a LN=0,snow=%%1
if not defined wind (set /a wind_E+=1)else call :smile)
echo 共%snow%行 %TAT%个字符
if defined wind_E echo 其中包含%wind_E%个空行
pause 1>nul&& exit
:smile
if defined wind (set wind=!wind:~1!&& set /a LN+=1&& goto :smile)else set /a TAT+=LN
论坛跳转: