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

[转贴] 批处理最大限度原样输出含特殊字符的指定行内容

  原始出处:http://www.cn-dos.net/forum/viewthread.php?tid=30815

  在CMD中,对含特殊字符的文本内容的输出处理一直是件很令人头痛的事情:如果要兼容特殊字符,一般会用引号把内容括起来再输出,但是这样一来,就会在所有输出行的首尾都添加了引号,如果对输出后的引号十分在意的话,这个方案就没法实行了。可是,除了这个方案之外,似乎没有别的方案能完美地解决这个难题。(注:完美方案请参考23楼bjsh的代码)

  最近这段时间略有闲暇,把这个问题又拿出来思考了一阵子,几经修改,就有了代码1,发出来让大家测试一下:

  代码1:
  1. @echo off
  2. :: 思路:把所有的特殊符号转义之后输出
  3. :: 所受限制:要处理的文件不能用引号括起来;
  4. cd.>output.txt
  5. for /f "delims=" %%i in ('findstr /n .* test.txt') do (
  6.     set "str=%%i"
  7.     call set "str=%%str:*:=%%"
  8.     if defined str (call :output) else echo.>>output.txt
  9. )
  10. start output.txt
  11. exit
  12. :output
  13. set "str=%str:^=^^%"
  14. set "str=%str:>=^>%"
  15. set "str=%str:<=^<%"
  16. set "str=%str:|=^|%"
  17. set "str=%str:&=^&%"
  18. set "str=%str:"=^"%"
  19. call echo.%%str%%>>output.txt
  20. goto :eof
复制代码


  修改自21楼bjsh的代码如下,个人认为是比较完美的方案了:

  代码2:
  1. @echo off
  2. cd.>output.txt
  3. for /f "delims=" %%i in ('findstr /n .* test.txt') do (
  4.     set "str=%%i"
  5.     call set "str=%%str:*:=%%"
  6.     if defined str (call :output) else echo.>>output.txt
  7. )
  8. start output.txt
  9. exit
  10. :output
  11. set "str=%str:^=^^%"
  12. set "str=%str:"=%"
  13. set "str=%str:>=^>%"
  14. set "str=%str:<=^<%"
  15. set "str=%str:&=^&%"
  16. set "str=%str:|=^|%"
  17. set "str=%str:="%"
  18. (call echo.%%str%%)>>output.txt
  19. goto :eof
复制代码
  最完美的代码如下(来自23楼bjsh的代码,本人仅作少量改动):

  代码3:
  1. @echo off
  2. cd.>output.txt
  3. for /f "delims=" %%i in ('findstr /n .* test.txt') do (
  4.         set "var=%%i"
  5.         setlocal enabledelayedexpansion
  6.         set var=!var:*:=!
  7.         (echo.!var!)>>output.txt
  8.         endlocal
  9. )
  10. 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 语句做调换,否则,仍然会导致感叹号被识别为变量引用符号,从而被抛弃掉。
尺有所短寸有所长,学好批处理没商量;
考虑问题复杂化,解决问题简洁化。

心在天山,身老沧州。

返回列表