当前位置: 首页 > news >正文

.bat批处理(九):替换带有等号=的字符串的子串

文章目录

  • 前言
  • 遇到的问题
    • 0x00 带有 = 的字符串
    • 0x01 带有 = 的想要被替换的子串
    • 0x02 尝试用转义字符来处理
  • 稳扎稳打的解决方案
  • 最终方案
    • 0x00 首先将 `=` 替换成一个原串中不可能出现的字符或者序列
    • 0x01 用这个不能出现序列替换我们之前要查找替换子串中的 `=`
    • 0x02 将第1步结束获得的替换结果作为原串,将其中的 `β` 替换成 `A`
    • 0x03 将第3步结果的子串作为原串,将其中的 `α` 替换为 `=`
  • 代码实现
  • 总结

前言

今天写这篇记录要解决的问题来源于最近一名读者的提问,之前写过一篇名为《.bat批处理(六):替换字符串中匹配的子串》的总结文章,结果有读者在评论区提问说,如果想要替换的子串中包含等号 =,那么就无法替换了,问有没有什么办法可以解决。遇到这个问题的第一感觉应该挺好处理的吧,如果批处理程序在替换操作中认为等号 = 比较特殊,那就加个转义字符应该就可以了,但事实却证明这种想法有些天真了。

在尝试多次失败之后,我意识到事情远没有想象的那么简单,开始在网上寻找解决方案,结果有些让人意外,绝大多数人都说这是 SET 命令的执行规则决定的,无法实现这种需求。当要替换的子串中包含 = 时,第一个 = 就会被认为是替换语法中的 =,进而导致无法得到正确的结果,即使是使用转义字符都无法完成正确替换,加入的转义字符会影响匹配,导致替换失败。还有一些人建议用其他工具来完整这种需求,比如记事本的替换功能 O(∩_∩)O

遇到的问题

看了上面的叙述,可能有些小伙伴对我所说的问题还没有太直观的认识,接下来我们举个例子来说一下这个问题究竟是怎样产生的。

0x00 带有 = 的字符串

首先需要被替换的字符串中要包含等号,我们来定义一个这样的变量:

set STR=abcdo=ocar12a=ajdjko=ot

变量的名字是 STR,变量的值是 abcdo=ocar12a=ajdjko=ot,其中包含了三个 =

0x01 带有 = 的想要被替换的子串

确定一下我们想要替换的子串 o=o,假如我们想把它替换成字母 A,按照一般的替换规则X:Y=Z,在 X 串中寻找到 Y 串之后把它替换成 Z 串,实现的代码如下:

@echo off

set STR=abcdo=ocar12a=ajdjko=ot
set RESULT=%STR:o=o=A%

echo %RESULT%
pause > nul

运行之后的结果是:

abcdo=A=o=Acar12a=ajdjko=A=o=At

和我们想法不一样,我们本来想把 o=o 替换成 A,但是从结果来看应该是把 o 替换成了 o=A,原因就是我们选择的被替换中的子串 o=o 包含一个 =,而这个 = 被当成了替换语法 X:Y=Z 中的 =,所以就不对了。

0x02 尝试用转义字符来处理

很多语言中都有转义字符,比如 Markdown 语法中的反斜杠 \,在 Markdown 语法中被星号 * 包裹的文字是倾斜的,但是如果想正常的输出一个 * 怎么办呢?就需要在 * 前面加一个反斜杠 \,变成 \*,这样 * 原本的倾斜文字的作用就被转义了,变成了一个普通的输出字符。

在批处理中也有转义字符的概念,它就是 ^,我们知道在批处理中 >| 等符号都是有特殊用处的,所以不能简单的输出,比如 echo > 是无法输出一个大于号的,要写成 echo ^> 才能正常输出一个 > 符号。

我们就利用这个转义字符来告诉替换命令,被替换的子串中的 = 是一个普通字符,不能作为替换规则的一部分,所以被替换的子串写成了 o^=o,我们实现下面的代码,看看能不能达到目的:

@echo off

set STR=abcdo=ocar12a=ajdjko=ot
set RESULT=%STR:o^=o=A%

echo %RESULT%
pause > nul

运行之后结果如下:

abcdo=ocar12a=ajdjko=ot

与替换前对比发现没有任何变化,看来转义字符的想法没能帮助我们解决问题,还是想想其他的办法吧。

稳扎稳打的解决方案

既然 = 这么特殊,我们就先想办法干掉等号,直接替换的方式不好使,我们可以一个字符一个字符的判断啊,虽然麻烦一点,但是解决问题才是最重要的。

既然要一个个的字符去判断,就需要遍历原字符串,最简单的可以使用字符串分割啊,语法为 原串:~偏移,长度 就可以了,如果不太清楚可以参考一下 《.bat批处理(三):变量声明、设置、拼接、截取》,截取第一个字符的语法是 原串:~0,1, 截取第二个字符的语法是 原串:~1,1,以此类推。

具体的思路就是我们先判断第一个字符,如果是 = 就进行替换,如果不是 = 就放到结果字符串里,然后继续判断第二个字符进行操作,最后所有的字符处理一遍就完成了替换。

需要使用 goto 语句来写一个循环,代码逻辑比较简单,就是遍历所有字符,是 = 就替换,不是 = 就保留,假设我们先把 = 替换成 #,实现的代码如下:

@echo off

set STR=abcdo=ocar12a=ajdjko=ot
set CURSTR=%STR%
set RESULT=

:next
if "%CURSTR%" equ "" goto end
set a=%CURSTR:~0,1%

if "%a%" equ "=" (set RESULT=%RESULT%#) else (set RESULT=%RESULT%%a%)
set CURSTR=%CURSTR:~1%
goto next

:end
echo source string is %STR%
echo result string is %RESULT%
pause > nul

:next 是循环的入口,每次截取第一个字符,判断是 = 就在结果中拼接 # 字符,相当于完成了替换,如果字符不是 = ,就将字符直接拼接到结果中,操作之后将原串的第一个字符删除形成新的原串,然后再判断第一个字符,以此类推,直到原串为空,运行结果如下:

source string is abcdo=ocar12a=ajdjko=ot
result string is abcdo#ocar12a#ajdjko#ot

最终方案

事情到了这里好像还没完,在实际操作中有些情况不是替换一个 =,往往是替换的内容中包含 =,上面将 = 替换成 # 不具有通用型,如果是一开始的请求,将 o=o替换成 A 就不能这样写了,就应该是每次判断3个字符了,写起来有些麻烦,批处理中没有获得字符串长度的函数,需要自己实现一个,如果是100个字符的被替换串,那代码就很难写了。

既然 = 都能被我们替换掉,肯定有办法实现上面我们这种将 o=o替换成 A 的要求,下面我们就列举一种通用的处理方法。

0x00 首先将 = 替换成一个原串中不可能出现的字符或者序列

这步替换可能最后需要还原的,所以要求我们替换成的目标序列不能在原串中出现,比如我们上面把 = 替换成了 #, 如果原串中有 # 就会弄混了,不能确定是原来字符串中就存在的 #,还是由 = 变成的 #

这个序列我们可以定义的变态一点,比如把 = 替换成 ###i#am#happy###,我们把它记作 α

0x01 用这个不能出现序列替换我们之前要查找替换子串中的 =

我们之前要查找替换的子串是 o=o,那么替换之后形成 o###i#am#happy###o,我们把它记作 β

0x02 将第1步结束获得的替换结果作为原串,将其中的 β 替换成 A

其实就是把第1步替换完结果作为原串,把其中的 o###i#am#happy###o 也就是原来的 o=o 替换成 A

0x03 将第3步结果的子串作为原串,将其中的 α 替换为 =

这一步就是处理那些虽然是 =,但是这个 = 不是我要替换的结果子串中的,所以要还原

代码实现

步骤梳理清楚了,下面来写代码,按照步骤一步步写就可以了:

@echo off

rem 第一步
set CORESTR=###i#am#happy###
set STR=abcdo=ocar12a=ajdjko=ot
set CURSTR=%STR%
set RESULT1=

:next1
if "%CURSTR%" equ "" goto end1
set a=%CURSTR:~0,1%

if "%a%" equ "=" (set RESULT1=%RESULT1%%CORESTR%) else (set RESULT1=%RESULT1%%a%)
set CURSTR=%CURSTR:~1%
goto next1

:end1
echo source1 string is %STR%
echo result1 string is %RESULT1%
pause > nul


rem 第 2 步
set CORESTR=###i#am#happy###
set STR=o=o
set CURSTR=%STR%
set RESULT2=

:next2
if "%CURSTR%" equ "" goto end2
set a=%CURSTR:~0,1%

if "%a%" equ "=" (set RESULT2=%RESULT2%%CORESTR%) else (set RESULT2=%RESULT2%%a%)
set CURSTR=%CURSTR:~1%
goto next2

:end2
echo source2 string is %STR%
echo result2 string is %RESULT2%
pause > nul


rem 第3步,需要开启延迟变量
setlocal ENABLEDELAYEDEXPANSION
set RESULT3=!RESULT1:%RESULT2%=A!
echo result3 string is %RESULT3%
pause > nul


rem 第4步
set RESULT4=!RESULT3:%CORESTR%==!

echo finally result is %RESULT4%

运行之后的结果为:

source1 string is abcdo=ocar12a=ajdjko=ot
result1 string is abcdo###i#am#happy###ocar12a###i#am#happy###ajdjko###i#am#happy###ot
source2 string is o=o
result2 string is o###i#am#happy###o
result3 string is abcdAcar12a###i#am#happy###ajdjkAt
finally result is abcdAcar12a=ajdjkAt

这次终于替换成功了,o=o 被成功替换成了字母 A,代码中用到了延迟变量,主要是为了实现被替换字符串是变量的情况,不清楚延迟变量的用法可以简单查询一下,至此文章开头提出的问题我们就成功解决了,虽然路途有些坎坷。

总结

  1. 批处理程序中的 = 比较特殊,使用常规的 X:Y=Z 的语法不能替换包含 = 的子串
  2. 遇到上述情况可以将字符串切割,采用逐个字符比较的方式,将 = 替换成其他字符再进行后续操作
  3. 有时候也不必非得使用批处理来替换包含 = 的字符串,随便一个文本工具,比如记事本都可以文本进行替换
  4. 如果非得用命令解决,也可以使用从 linux 的 sed 命令移植到 windows 的 sed.exe 程序来很方便的进行替换
  5. 使用 sed 命令的语法是 echo abcdo=ocar12a=ajdjko=ot | sed -e "s/o=o/A/g",一步就可以完成了文章开头的需求了
  6. 如果你暂时没有 sed.exe 程序,可以点击这个链接 sed.exe程序 下载,若不是在同一目录使用,记得将命令目录添加到环境变量中

时间慢慢地磨去了年少轻狂,也渐渐地沉淀了冷暖自知。

相关文章:

  • 简单聊聊C/C++中的左值和右值
  • C++11在左值引用的基础上增加右值引用
  • 汇编指令入门级整理
  • 使用c++filt命令还原C++编译后的函数名
  • 配置Beyond Compare 4作为git mergetool来解决git merge命令导致的文件冲突
  • git在回退版本时HEAD~和HEAD^的作用和区别
  • 对称加密、非对称加密、公钥、私钥究竟是个啥?
  • 认证、HTTPS、证书的基本含义
  • 码龄10年工作6年的搬砖小哥,最常访问的学习网站都在这里了
  • C++中的std::lower_bound()和std::upper_bound()函数
  • 根证书的应用和信任基础
  • Shell脚本中获取命令运行结果、特殊变量使用、条件判断等常用操作
  • gdb调试解决找不到源代码的问题
  • GDB调试指北大全
  • 小白眼中的docker究竟是个什么东西
  • 自己简单写的 事件订阅机制
  • Angular 响应式表单 基础例子
  • CSS中外联样式表代表的含义
  • 半理解系列--Promise的进化史
  • 极限编程 (Extreme Programming) - 发布计划 (Release Planning)
  • 技术发展面试
  • 深入 Nginx 之配置篇
  • 腾讯优测优分享 | Android碎片化问题小结——关于闪光灯的那些事儿
  • 用Visual Studio开发以太坊智能合约
  • Hibernate主键生成策略及选择
  • linux 淘宝开源监控工具tsar
  • 策略 : 一文教你成为人工智能(AI)领域专家
  • ​ 无限可能性的探索:Amazon Lightsail轻量应用服务器引领数字化时代创新发展
  • ​​​​​​​sokit v1.3抓手机应用socket数据包: Socket是传输控制层协议,WebSocket是应用层协议。
  • (2022版)一套教程搞定k8s安装到实战 | RBAC
  • (Matalb时序预测)WOA-BP鲸鱼算法优化BP神经网络的多维时序回归预测
  • (TipsTricks)用客户端模板精简JavaScript代码
  • (八)Spring源码解析:Spring MVC
  • (附源码)计算机毕业设计SSM保险客户管理系统
  • (附源码)流浪动物保护平台的设计与实现 毕业设计 161154
  • (论文阅读30/100)Convolutional Pose Machines
  • (深入.Net平台的软件系统分层开发).第一章.上机练习.20170424
  • (四)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • .NET 使用配置文件
  • .NET开源全面方便的第三方登录组件集合 - MrHuo.OAuth
  • .NET学习教程二——.net基础定义+VS常用设置
  • .set 数据导入matlab,设置变量导入选项 - MATLAB setvaropts - MathWorks 中国
  • [Android]使用Retrofit进行网络请求
  • [Angular] 笔记 16:模板驱动表单 - 选择框与选项
  • [AutoSar]BSW_Memory_Stack_004 创建一个简单NV block并调试
  • [BZOJ1877][SDOI2009]晨跑[最大流+费用流]
  • [C#] 基于 yield 语句的迭代器逻辑懒执行
  • [C#]DataTable常用操作总结【转】
  • [Deep Learning] 神经网络基础
  • [DNS网络] 网页无法打开、显示不全、加载卡顿缓慢 | 解决方案
  • [Firefly-Linux] RK3568修改控制台DEBUG为普通串口UART
  • [go 反射] 进阶
  • [LeetCode系列]子集枚举问题[无重复元素]
  • [LWC] Components Communication
  • [nlp] 多语言大模型不同语种/语系数据的数据配比调节