![从字符前面、后面和内部提取文本](https://linux22.com/image/1571865/%E4%BB%8E%E5%AD%97%E7%AC%A6%E5%89%8D%E9%9D%A2%E3%80%81%E5%90%8E%E9%9D%A2%E5%92%8C%E5%86%85%E9%83%A8%E6%8F%90%E5%8F%96%E6%96%87%E6%9C%AC.png)
我有一个变量(%~1
),设置为如下值:Hello there this is a block: [C]Inside of Block[/C] Did you like it?
我想将其分成三个变量:
Front=hello there this is a block:
Block=Inside of Block
Back=Did you like it?
我尝试使用这个:
:call
set var=%~1
set Front=%var:,"[C]"=&:%
set Back=%var:*"[/C]=%
Set var=%var:,"[/C]"=&:%
Set var=%var:*"[C]=%
set Inside=%var%
但它不起作用。可能是因为这是在call
一个循环内部调用的部分for
(使用 EnableDelayedExpansion)。但整个字符串都设置为每个变量。可以这样做吗?
答案1
如果您能提供一些链接来解释您尝试使用的技术,我会很有帮助。幸运的是,我熟悉这项技术。
您正在尝试使用变量扩展 find/replace 将注释注入字符串,以便提取字符串的开头,直到某个子字符串。我更熟悉使用REM
,但您使用:
标签作为伪注释也应该有效。
我将通过一个简单示例来说明其工作原理。我已ECHO ON
列出战略路线,以便您了解替换的工作原理。这要求我使用REM
而不是:
因为标签未 ECHO。
@echo off
setlocal
set "var=String Before<split here>String After"
echo on
set "Before=%var:<split here>="&rem %
@set before
set "After=%var:*<split here>=%"
@set after
- 输出 -
C:\test>set "Before=String Before" & rem String After
Before=String Before
C:\test>set "After=String After"
After=String After
周围的额外空格&
是 cmd.exe 回显该行时产生的产物,它们实际上不是由查找/替换引入的。但您应该能够看到该技术是如何工作的。
我不明白为什么您在搜索字符串中包含逗号和引号 - 它们不在您的起始字符串中,因此没有任何内容会被替换。
保存初始值时使用引号很重要,%~1
以防止有毒字符。
最后,在后续赋值中使用引号也很重要。第一个引号位于变量名之前,结束引号由替换项插入,位于 之前&:
。
因为您使用的是:
而不是REM
,所以没有必要在 之后添加空格:
。
var
这是工作代码。请注意,我通过使用临时值来消除对额外变量的需求,Back
直到我准备好获取最终的 Back 值。
@echo off
setlocal
call :extract "Hello there this is a block: [C]Inside of Block[/C] Did you like it?"
echo Front = "%Front%"
echo Inside = "%Inside%"
echo Back = "%Back%"
exit /b
:extract
set "Back=%~1"
set "Front=%Back:[C]="&:%
set "Back=%Back:*[C]=%"
set "Inside=%Back:[/C]="&:%
set "Back=%Back:*[/C]=%"
exit /b
- 输出 -
Front = "Hello there this is a block: "
Inside = "Inside of Block"
Back = " Did you like it?"
请注意我在输出中使用了引号 - 它们不在实际存储的值中。而是由 ECHO 命令引入的,用于显示尾随/前导空格的存在,并且它们还可以防止有毒字符。
上述技术有一些限制:
- 该
%~1
值不应包含任何引号,否则可能会出现有毒字符破坏结果的风险 - 该
%~1
值不能包含换行符(0x0A)或回车符(0x0D)。 - 您要替换的子字符串(
[C]
就[/C]
您而言)不能以~
、*
或开头%
。 - 您要替换的子字符串不得包含
=
任何内容
这是一个完全不相关的 JREPL.BAT 解决方案
如果你喜欢使用正则表达式,那么你可以使用我的执行文件- 纯脚本实用程序(混合 JScript/batch),可在 XP 及更高版本的任何 Windows 机器上本地运行,无需任何第三方 .exe 文件。
对于此应用程序来说,JREPL 解决方案会比纯批处理慢一点,但它有两个很大的优点:
- 逻辑更直接,只要你理解正则表达式
- 没有字符限制。
%~1
仍然不能包含回车符或换行符。但变量可以,并且 JREPL 可以很好地处理这些字符。
如果您只需要提取单个值,那么解决方案非常简单 - JREPL 能够将结果存储在环境变量中。下面的代码将捕获块内的值:
@echo off
setlocal
call :extract "Hello there this is a block: [C]Inside of Block[/C] Did you like it?"
echo Inside = "%Inside%"
exit /b
:extract
set "Inside=%~1"
call jrepl "\[C](.*)\[/C]" "$txt=$1" /s Inside /jmatchq /rtn Inside
exit /b
- 输出 -
Inside = "Inside of Block"
但是您想要捕获 3 个值。您可以进行三次单独的 JREPL 调用,但这样做效率不高。在下面的代码中,我VariableName=
在适当的位置插入和换行符,然后FOR /F
迭代并存储这三个结果。
@echo off
setlocal
call :extract "Hello there this is a block: [C]Inside of Block[/C] Did you like it?"
echo Front = "%Front%"
echo Inside = "%Inside%"
echo Back = "%Back%"
exit /b
:extract
set "Front=%~1"
for /f "delims=" %%A in (
'jrepl "^|\[C]|\[/C]" "Front=|\nInside=|\nBack=" /s Front /t "|" /xseq'
) do set "%%A"
exit /b
- 输出 -
Front = "Hello there this is a block: "
Inside = "Inside of Block"
Back = " Did you like it?"
JREPL 实用程序内置有广泛的帮助。
JREPL /?
将列出所有帮助。JREPL /?options
将简要总结所有可用选项JREPL /?/T
将描述电视我使用的 translate 选项。您可以对/?/XSEQ
和执行相同的操作/?/S
一旦将 JREPL 纳入您的工具库,您很可能会发现它的很多用途。JREPL 在处理文本文件时确实很出色 - 它比任何纯批处理解决方案都更快、更强大。
答案2
一般来说,批处理文件对于处理这种复杂的事情不是很有效。也不可能像你尝试的那样使用变量替换。
话虽如此,此解决方案应该适用于任何包含单个块或根本不包含块的字符串。这不适用于多个块。它也只适用于单字符块标签(即,,[C]
) 。如果您的文本中包含不属于块的字符,它也不会起作用。[b]
[9]
[
:Split
for /f "delims=[ tokens=1-3" %%a IN ("%~1") DO (
CALL:Set "%%~a" "%%~b" "%%~c"
)
GOTO:EOF
:Set
SET Front=%~1
SET Inside=%~2
SET Back=%~3
SET Block=
IF NOT "%Inside%"=="" (
SET Block=%Inside:~0,1%
SET Inside=%Inside:~2%
)
IF NOT "%Back%"=="" (
SET Back=%Back:~3%
)
GOTO:EOF
调用Split
将按字符分割字符串[
并将其传递给Set
调用。Set
然后调用将删除开始和结束的块标记。
进一步阅读: 变量编辑/替换 - SS64