中国DOS联盟论坛

中国DOS联盟

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

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

游客:  注册 | 登录 | 命令行 | 会员 | 搜索 | 上传 | 帮助 »
中国DOS联盟论坛 » DOS批处理 & 脚本技术(批处理室) » [讨论]最大限度原样输出含特殊字符的指定行内容
« [1] [2] [3] »
作者:
标题: [讨论]最大限度原样输出含特殊字符的指定行内容 上一主题 | 下一主题
namejm
荣誉版主

batch fan


积分 5226
发帖 1737
注册 2006-3-10
来自 成都
状态 离线
『楼 主』:  [讨论]最大限度原样输出含特殊字符的指定行内容

  在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的分析;
  ③ 在 :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 语句做调换,否则,仍然会导致感叹号被识别为变量引用符号,从而被抛弃掉。

[ Last edited by namejm on 2007-8-14 at 09:27 PM ]

   此帖被 +40 点积分       点击查看详情   
评分人:【 lxmxn 分数: +20  时间:2007-5-17 22:42
评分人:【 zh159 分数: +15  时间:2007-5-18 00:11
评分人:【 huzixuan 分数: +4  时间:2007-5-18 12:36
评分人:【 cutebe 分数: +1  时间:2009-10-17 13:38




尺有所短,寸有所长,学好CMD没商量。
考虑问题复杂化,解决问题简洁化。
2007-5-17 21:14
查看资料  发短消息 网志   编辑帖子  回复  引用回复
lxmxn
版主




积分 11386
发帖 4938
注册 2006-7-23
状态 离线
『第 2 楼』:  

感谢 namejm 兄的原创,在工作这么忙的情况下还可以顾及论坛,实感欣慰。

经过短暂的测试发现没有任何输出错误。

[ Last edited by lxmxn on 2007-5-17 at 11:16 PM ]

2007-5-17 22:44
查看资料  发送邮件  发短消息 网志   编辑帖子  回复  引用回复
ieutk
初级用户




积分 107
发帖 48
注册 2006-11-30
状态 离线
『第 3 楼』:  

好东东,可惜看不懂啊!

2007-5-17 23:25
查看资料  发送邮件  发短消息 网志   编辑帖子  回复  引用回复
zh159
金牌会员




积分 3687
发帖 1467
注册 2005-8-8
状态 离线
『第 4 楼』:  

除了引号,其他的其实都好办

2007-5-18 00:11
查看资料  发短消息 网志   编辑帖子  回复  引用回复
bjsh
银牌会员





积分 2000
发帖 621
注册 2007-1-1
状态 离线
『第 5 楼』:  

以前也写过类似的:
和namejm兄的差不多;
不过没考虑过某一行都是空格的情况;
最后一句是抄namejm的;
echo.%var%的用法实在妙极.
@echo off
for /f "tokens=1 delims=" %%a in ('findstr /n .* test.txt') do set "var=%%a" & call :change
goto :eof
:change
set "var=%var:^=^^%"
set "var=%var:>=^>%"
set "var=%var:<=^<%"
set "var=%var:&=^&%"
set "var=%var:|=^|%"
set "var=%var:*:=%"
echo.%var%
这个解决了"问题;
诸位也帮忙测试下;

2007-5-18 08:54
查看资料  发送邮件  发短消息 网志   编辑帖子  回复  引用回复
lxmxn
版主




积分 11386
发帖 4938
注册 2006-7-23
状态 离线
『第 6 楼』:  

To bjsh:

测试发现点小问题,请看下面我的测试文本。

test.txt

  Quote:
~ ! @ # $ % ^ & * ( () " ok " No " <>nul
set ok=^

屏幕显示

  Quote:
此时不应有 >。



2007-5-18 12:36
查看资料  发送邮件  发短消息 网志   编辑帖子  回复  引用回复
bjsh
银牌会员





积分 2000
发帖 621
注册 2007-1-1
状态 离线
『第 7 楼』:  

唉;三个引号的问题啊;
就好像
echo fsdg"dsgsa"gds" >55.txt
不会写到55.txt里一样;
我在想想吧;
难道真得非写成不可见字符.

2007-5-18 12:53
查看资料  发送邮件  发短消息 网志   编辑帖子  回复  引用回复
ttyp
初级用户





积分 180
发帖 84
注册 2006-9-7
状态 离线
『第 8 楼』:  

结果不对啊

aoueo

;euou%^>
::::aeui
   
:::E2uo alejou 3<o2io|
^aue||%ou

!aue!
!str!auoeu!ueo &&
ueyi^^^^aueuo2
=^

第一行“变成乱码,多了最后一行

2007-5-18 13:07
查看资料  发送邮件  发短消息 网志   编辑帖子  回复  引用回复
lxmxn
版主




积分 11386
发帖 4938
注册 2006-7-23
状态 离线
『第 9 楼』:  

To ttyp:

第一行的 " 是楼主故意变成那个特殊字符的。

你的测试文本是不是复制楼主的测试文本啊?

我这里测试完全正常。

2007-5-18 13:15
查看资料  发送邮件  发短消息 网志   编辑帖子  回复  引用回复
ttyp
初级用户





积分 180
发帖 84
注册 2006-9-7
状态 离线
『第 10 楼』:  

是复制的,第一行开始没看清楚。
但是最后多一行很奇怪
我的系统是W2K+SP4

2007-5-18 13:21
查看资料  发送邮件  发短消息 网志   编辑帖子  回复  引用回复
namejm
荣誉版主

batch fan


积分 5226
发帖 1737
注册 2006-3-10
来自 成都
状态 离线
『第 11 楼』:  



  Quote:
Originally posted by bjsh at 2007-5-18 12:53:
唉;三个引号的问题啊;
就好像
echo fsdg"dsgsa"gds" >55.txt
不会写到55.txt里一样;
我在想想吧;
难道真得非写成不可见字符.

  经过测试,应该是引号为奇数个的时候才会有这种情况,这是 call 语句调用子过程的时候 echo 语句的一大缺陷,可能只有替换为其他字符才行。

  顶楼的代码已经精简了部分语句,set "str=%~1" 完全没有必要存在,呵呵,我的思路复杂过头了。

[ Last edited by namejm on 2007-5-18 at 01:47 PM ]



尺有所短,寸有所长,学好CMD没商量。
考虑问题复杂化,解决问题简洁化。
2007-5-18 13:26
查看资料  发短消息 网志   编辑帖子  回复  引用回复
ttyp
初级用户





积分 180
发帖 84
注册 2006-9-7
状态 离线
『第 12 楼』:  

哦,明白了,是我一直在测试P处理,注释了EXIT导致的

2007-5-18 13:27
查看资料  发送邮件  发短消息 网志   编辑帖子  回复  引用回复
bjsh
银牌会员





积分 2000
发帖 621
注册 2007-1-1
状态 离线
『第 13 楼』:  



  Quote:
Originally posted by namejm at 2007-5-18 01:26 PM:

  经过测试,应该是引号为奇数个的时候才会有这种情况,这是 call 语句调用子过程的时候 echo 语句的一大缺陷,可能只有替换为其他字符才行。 ...

是的;是奇数个的时候这样;
echo dsg^"sdgsad^"gdgasdg^" >5.txt
就可以了;

其实"也算特殊字符了;

重新修改一下
@echo off
set var=
for /f "delims=" %%a in ('findstr /n .* test.txt') do (
set "var=%%a"
call set "var=%%var:"=%%"
call :change
)
goto :eof
:change
set "var=%var:^=^^%"
set "var=%var:>=^>%"
set "var=%var:<=^<%"
set "var=%var:&=^&%"
set "var=%var:|=^|%"
set "var=%var:*:=%"
if not defined var echo. & goto :eof
call set "var=%%var:="%%"
echo.%var%
再测试看还有什么问题

2007-5-18 13:49
查看资料  发送邮件  发短消息 网志   编辑帖子  回复  引用回复
bjsh
银牌会员





积分 2000
发帖 621
注册 2007-1-1
状态 离线
『第 14 楼』:  



  Quote:
c:\>set "var=~ ! @ # $ % ^ & * ( () " ok " No " <>nul"
此时不应有 >。



  Quote:
c:\>for /f "delims=" %a in (test.txt) do set "var=%a"

c:\>set "var=~ ! @ # $ % ^ & * ( () " ok " No " <>nul"
c:\>set var
var=~ ! @ # $ % ^ & * ( () " ok " No " <>nul

test.txt内容

  Quote:
~ ! @ # $ % ^ & * ( () " ok " No " <>nul

发现两者的不同了吗?
前者 显示 此时不应有 >
而后者只是利用for把那段字符转换为%a再赋值给var;就不会报错了;
有意思啊;

2007-5-18 14:08
查看资料  发送邮件  发短消息 网志   编辑帖子  回复  引用回复
namejm
荣誉版主

batch fan


积分 5226
发帖 1737
注册 2006-3-10
来自 成都
状态 离线
『第 15 楼』:  

  当引号为奇数个的时候,echo的结果不会有什么错误,但是,当引号个数为偶数个的时候,就会出错了。解决的办法是 echo.%var% 改为 call echo.%%var%%,但是这样一来,如果测试内容为 ~ ! @ # $ % ^ & * ( () " ok " No " a>a 的时候,会把最后一个引号之后的所有内容都抛弃掉,原因暂时不明。

————————————————————————————————
  以上言论属于使用的测试代码有误,导致结论出错。


[ Last edited by namejm on 2007-5-18 at 02:53 PM ]



尺有所短,寸有所长,学好CMD没商量。
考虑问题复杂化,解决问题简洁化。
2007-5-18 14:09
查看资料  发短消息 网志   编辑帖子  回复  引用回复
« [1] [2] [3] »
请注意:您目前尚未注册或登录,请您注册登录以使用论坛的各项功能,例如发表和回复帖子等。


可打印版本 | 推荐给朋友 | 订阅主题 | 收藏主题



论坛跳转: