从字符前面、后面和内部提取文本

从字符前面、后面和内部提取文本

我有一个变量(%~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

相关内容