Board logo

标题: [转贴] 批处理最大限度原样输出含特殊字符的指定行内容 [打印本页]

作者: namejm    时间: 2009-5-18 22:56     标题: 批处理最大限度原样输出含特殊字符的指定行内容

  原始出处: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 语句做调换,否则,仍然会导致感叹号被识别为变量引用符号,从而被抛弃掉。
作者: SunTB    时间: 2009-5-25 13:21

看了本帖 又回头看CN-DOS原帖
现在正在看bjsh版主的
简析环境变量和变量延迟特殊字符以及中介法的微妙关系

一直以来对变量延迟都没怎么关注 虽然每天都在用

看来得好好学学了
作者: 风之语故乡    时间: 2011-7-27 01:18

  1. @echo off
  2. setlocal enabledelayedexpansion
  3. for /f "delims=" %%i in ('findstr /n .* 520.txt') do (
  4. set "var=%%i"
  5. set var=!var:*:=!
  6. set /a j+=1
  7. if !j! EQU 1 (set "Mstr=!var!") else (set "Mstr=!Mstr!&echo.!var!")
  8. )
  9. echo %Mstr%
  10. 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"
  1. IF EXIST "!curPath!\tmpchk.txt" (
  2. FOR /F %%A IN ('FINDSTR .* "!curPath!\tmpchk.txt"') DO (
  3.   ECHO %%A
  4.   FOR %%i IN ("%%A") DO (
  5.    CALL :SHOW %%i
  6.    SET "cPath=%%~dpi"
  7.   )
  8.   COPY /Y "%%A" "!curPath!\copy">NUL
  9.   IF EXIST "!curPath!\tmp.c" (DEL /F /S /Q "!curPath!\tmp.c">NUL)
  10.   FOR /F "tokens=1* delims=:" %%i IN ('FINDSTR /N .* "%%A"') DO (
  11.    SET "str1=%%j"
  12.    IF DEFINED str1 (
  13.     IF /I "!str1:~0,8!" EQU "!var2!" (
  14.      CALL SET "str2=%%str1:!!var1!!=%%"
  15.      IF NOT "!str1!" EQU "!str2!" (CALL SET "str1=%%str1:!var1!=!var3!%%")
  16.      ECHO;!str1!>>"!curPath!\tmp.c"
  17.     ) ELSE (ECHO;%%j>>"!curPath!\tmp.c")
  18.    ) ELSE (ECHO.>>"!curPath!\tmp.c")
  19.   )
  20.   REN "!curPath!\tmp.c" !cName!
  21.   MOVE /Y "!curPath!\!cName!" !cPath!
  22. )
  23. )
复制代码

作者: wheat611    时间: 2013-12-17 11:04

回复 1# namejm


    老楼主还在么,您写的总结我觉得看懂了,但是代码还是改不对,贴在楼上了




欢迎光临 批处理之家 (http://www.bathome.net/) Powered by Discuz! 7.2