欢迎光临

木子屋

标题: 最大限度原样输出含特殊字符的指定行内容

作者: dnawo

日期: 2008-06-23 11:24 PM

分类: 操作系统

内容: 在CMD中，对含特殊字符的文本内容的输出处理一直是件很令人头痛的事情：如果要兼容特殊字符，一般会用引号把内容括起来再输出，但是这样一来，就会在所有输出行的首尾都添加了引号，如果对输出后的引号十分在意的话，这个方案就没法实行了。可是，除了这个方案之外，似乎没有别的方案能完美地解决这个难题。（注：完美方案请参考23楼bjsh的代码）
最近这段时间略有闲暇，把这个问题又拿出来思考了一阵子，几经修改，就有了代码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的代码如下，个人认为是比较完美的方案了：
复制内容到剪贴板 程序代码@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的代码，本人仅作少量改动)：
复制内容到剪贴板 程序代码@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的分析；
③ 在 :output 子过程中，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 语句做调换，否则，仍然会导致感叹号被识别为变量引用符号，从而被抛弃掉。

+ 查看当前日志评论 (0)

登录

────────────

木子屋

PJBlog3 v3.2.9.518 Inside.

Processed In 0.656 ms

返回

标题: 最大限度原样输出含特殊字符的指定行内容

你没有权限发表评论

────────────

木子屋

PJBlog3 v3.2.9.518 Inside.

Processed In 0.656 ms

返回

暂无评论

返回

────────────

木子屋

PJBlog3 v3.2.9.518 Inside.

Processed In 0.688 ms

返回