标题: [转贴] 批处理最大限度原样输出含特殊字符的指定行内容 [打印本页]
作者: namejm 时间: 2009-5-18 22:56 标题: 批处理最大限度原样输出含特殊字符的指定行内容
原始出处:http://www.cn-dos.net/forum/viewthread.php?tid=30815
在CMD中,对含特殊字符的文本内容的输出处理一直是件很令人头痛的事情:如果要兼容特殊字符,一般会用引号把内容括起来再输出,但是这样一来,就会在所有输出行的首尾都添加了引号,如果对输出后的引号十分在意的话,这个方案就没法实行了。可是,除了这个方案之外,似乎没有别的方案能完美地解决这个难题。(注:完美方案请参考23楼bjsh的代码)
最近这段时间略有闲暇,把这个问题又拿出来思考了一阵子,几经修改,就有了代码1,发出来让大家测试一下:
代码1:
- @echo off
- :: 思路:把所有的特殊符号转义之后输出
- :: 所受限制:要处理的文件不能用引号括起来;
- cd.>output.txt
- for /f "delims=" %%i in ('findstr /n .* test.txt') do (
- set "str=%%i"
- call set "str=%%str:*:=%%"
- if defined str (call :output) else echo.>>output.txt
- )
- start output.txt
- exit
-
- :output
- set "str=%str:^=^^%"
- set "str=%str:>=^>%"
- set "str=%str:<=^<%"
- set "str=%str:|=^|%"
- set "str=%str:&=^&%"
- set "str=%str:"=^"%"
- call echo.%%str%%>>output.txt
- goto :eof
复制代码
修改自21楼bjsh的代码如下,个人认为是比较完美的方案了:
代码2:- @echo off
- cd.>output.txt
- for /f "delims=" %%i in ('findstr /n .* test.txt') do (
- set "str=%%i"
- call set "str=%%str:*:=%%"
- if defined str (call :output) else echo.>>output.txt
- )
- start output.txt
- exit
-
- :output
- set "str=%str:^=^^%"
- set "str=%str:"=%"
- set "str=%str:>=^>%"
- set "str=%str:<=^<%"
- set "str=%str:&=^&%"
- set "str=%str:|=^|%"
- set "str=%str:="%"
- (call echo.%%str%%)>>output.txt
- goto :eof
复制代码
最完美的代码如下(来自23楼bjsh的代码,本人仅作少量改动):
代码3:- @echo off
- cd.>output.txt
- for /f "delims=" %%i in ('findstr /n .* test.txt') do (
- set "var=%%i"
- setlocal enabledelayedexpansion
- set var=!var:*:=!
- (echo.!var!)>>output.txt
- endlocal
- )
- start output.txt
复制代码
测试文件test.txt的内容(请注意:其中一个空行是以空格组成的):
"aou"eo
;euou%^>
::::aeui
:::E2uo alejou 3<o2io|
^aue||%ou
!aue!
aoue eou 2
!str!auoeu!ueo &&
euo 8
ueyi^^^^aueuo2
~ ! @ # $ % ^ & * ( () " ok " No " <>nul
set ok=^
关于代码1及代码2,分析如下:
① 按照一般的思路,for语句中引用变量,都是使用 setlocal enabledelayedexpansion 语句来启用变量延迟功能,但是,这个功能有个致命的缺陷:当要处理的字符串中含有感叹号的时候,会把感叹号对及其之间的所有字符串置换为空,所以,代码1和代码2抛弃 setlocal 方案,使用 call 一段子过程的方案;
② 如果要处理的文本含有奇数个引号的话,echo.%str%>>output.txt 语句将会出错,所以直接把引号替换为特殊的不可见字符之后再输出(也就是在代码中显示出来的黑框);感谢lxmxn的测试和bjsh的分析;
③ 在 utput 子过程中,set "str=%str:^=^^%" 一句必须放在所有替换语句之前,否则,将会把^重复替换,导致结果不准确;感谢 lxmxn 的测试;
④ 普通的 for 语句会忽略以分号打头的行内容,对空行也会忽略掉,所以,使用 findstr .* test.txt 语句来显示所有行(包括空行);delims=: 会把行首的所有冒号抛弃,所以,使用了 call set "str=%%str:*:=%%" 语句来避免这种情况;
⑤ (call echo.%%str%%)>>output.txt 语句中echo后紧跟的点号不能省略,否则,当行内容为空格的时候,输出后的内容将会显示echo的当前状态;使用call语句是为了兼容带引号的行;使用括号是为了能正确处理行尾是以空格分隔的单独的1~9这九个数字。
⑥ :output 标签段之所以不用 for %%i in (^^ ^> ^< ^| ^&) do call set "str=%%str:%%i=^%%i%%" 这样的语句,是因为这条替换语句并不能正确替换,看来for语句中的 call 延迟机制确实有点让人费解。
关于代码3,由于水平有限,只能做点肤浅而模糊的分析:在这段代码中,利用变量延迟功能来完整地获取特殊字符,并在适当的时候终止变量延迟,以避免因变量延迟过度而造成字符串被识别为变量的问题,实际上,这还是CMD预处理机制在起作用。值得注意的是,setlocal 语句的位置不能与 set 语句做调换,否则,仍然会导致感叹号被识别为变量引用符号,从而被抛弃掉。
作者: SunTB 时间: 2009-5-25 13:21
看了本帖 又回头看CN-DOS原帖
现在正在看bjsh版主的
简析环境变量和变量延迟特殊字符以及中介法的微妙关系
一直以来对变量延迟都没怎么关注 虽然每天都在用
看来得好好学学了
作者: 风之语故乡 时间: 2011-7-27 01:18
- @echo off
- setlocal enabledelayedexpansion
- for /f "delims=" %%i in ('findstr /n .* 520.txt') do (
- set "var=%%i"
- set var=!var:*:=!
- set /a j+=1
- if !j! EQU 1 (set "Mstr=!var!") else (set "Mstr=!Mstr!&echo.!var!")
- )
- echo %Mstr%
- pause>nul
复制代码
我的这个,是想将文本的内容放入指定变量中,但是,果然无法解决!号被抛弃的问题,
(因为我只能将变量延迟,不然无法让Mstr获得值),因而求教。。。。
作者: PowerShell 时间: 2013-8-31 08:21
楼主前辈,前辈楼主 你好,微软脚本出了三师弟powershell,尽解了大师兄bat的那些毛病,如下帖子:
http://www.bathome.net/viewthrea ... mp;page=1#pid136822
希望您常回来看看。
作者: wheat611 时间: 2013-12-17 11:03
本帖最后由 wheat611 于 2013-12-17 11:04 编辑
回复 4# PowerShell
看的我似懂非懂,但是我的代码还是不会改,叹号还是出不来,20行这一句 ECHO;!str1!>>"!curPath!\tmp.c"- IF EXIST "!curPath!\tmpchk.txt" (
- FOR /F %%A IN ('FINDSTR .* "!curPath!\tmpchk.txt"') DO (
- ECHO %%A
-
- FOR %%i IN ("%%A") DO (
- CALL :SHOW %%i
- SET "cPath=%%~dpi"
- )
-
- COPY /Y "%%A" "!curPath!\copy">NUL
-
- IF EXIST "!curPath!\tmp.c" (DEL /F /S /Q "!curPath!\tmp.c">NUL)
-
- FOR /F "tokens=1* delims=:" %%i IN ('FINDSTR /N .* "%%A"') DO (
- SET "str1=%%j"
- IF DEFINED str1 (
- IF /I "!str1:~0,8!" EQU "!var2!" (
- CALL SET "str2=%%str1:!!var1!!=%%"
- IF NOT "!str1!" EQU "!str2!" (CALL SET "str1=%%str1:!var1!=!var3!%%")
- ECHO;!str1!>>"!curPath!\tmp.c"
- ) ELSE (ECHO;%%j>>"!curPath!\tmp.c")
- ) ELSE (ECHO.>>"!curPath!\tmp.c")
- )
-
- REN "!curPath!\tmp.c" !cName!
- MOVE /Y "!curPath!\!cName!" !cPath!
- )
- )
复制代码
作者: wheat611 时间: 2013-12-17 11:04
回复 1# namejm
老楼主还在么,您写的总结我觉得看懂了,但是代码还是改不对,贴在楼上了
欢迎光临 批处理之家 (http://bbs.bathome.net/) |
Powered by Discuz! 7.2 |