【发布时间】:2013-01-15 06:07:04
【问题描述】:
我想将一个字符串分成两部分,不使用任何 for 循环。
例如,我在变量中有字符串:
str=45:abc
我想在一个变量中获得45,在另一个变量中获得abc。批处理文件可以吗?
模式就像 somenumber:somestring
【问题讨论】:
标签: batch-file dos
我想将一个字符串分成两部分,不使用任何 for 循环。
例如,我在变量中有字符串:
str=45:abc
我想在一个变量中获得45,在另一个变量中获得abc。批处理文件可以吗?
模式就像 somenumber:somestring
【问题讨论】:
标签: batch-file dos
你可以用不同的方式分割str。
for 循环,你不想使用它。
* 的尾随部分很容易(匹配任何内容,直到 ...)set "var2=%str:*:=%"
可以用一个讨厌的技巧来完成前导部分set "var1=%str::="^&REM #%
脱字符号需要插入符号,
所以有效地冒号将被替换为"&REM #
因此,在您的情况下,您在替换后得到了线路set "var1=4567"&REM #abcde
这分为两个命令
set "var1=4567"
REM #abcde`
完整的代码在这里:
set "str=4567:abcde"
echo %str%
set "var1=%str::="^&REM #%
set "var2=%str:*:=%"
echo var1=%var1% var2=%var2%
编辑 2:更稳定的前导部分
感谢 Dave 提出使用换行符的想法。
REM 技术对于带有引号和特殊字符的内容不是很稳定。
但是通过换行技巧,存在一个更稳定的版本,当 split 参数长于单个字符时也可以使用。
@echo off
setlocal enableDelayedExpansion
set ^"str=456789#$#abc"
for /F "delims=" %%a in (^"!str:#$#^=^
!^") do (
set "lead=%%a"
goto :break
)
:break
echo !lead!
解决方案 3:适应 dbenhams 答案
Dbenham 在他的解决方案中使用了带有管道的换行符。
这似乎有点过于复杂。
由于该解决方案使用了这样一个事实,即解析器在未转义的换行符之后删除了该行的其余部分(当在special character phase 之前或中找到它时)。
首先,冒号字符被替换为换行符,并带有延迟扩展替换。
这是允许的,换行符现在是变量的一部分。
然后set lead=%lead% 行去掉尾部。
最好不要在这里使用扩展语法,因为如果引号是字符串的一部分,set "lead=%lead%" 会中断。
setlocal enableDelayedExpansion
set "str=45:abc"
set ^"lead=!str::=^
!"
set lead=%lead%
echo "!lead!"
【讨论】:
^&REM - 没有哈希。
/?
你可以试试这个。如果它固定,冒号左侧的数字将始终为 2,而右侧的数字将始终为 3。假设您的 str 具有该值,则以下代码应该可以工作。
set "str=45:abc"
echo %str%
set var1=%str:~0,2%
set var2=%str:~3,3%
echo %var1% %var2%
随时通知我。 :)
【讨论】:
避免使用 FOR 循环似乎毫无意义,但它确实使问题变得有趣。
正如 jeb 所指出的,使用!str:*:=! 可以轻松获取结尾部分。
棘手的部分是主要部分。这是jeb的解决方案的替代方案。
您可以使用以下语法在变量中插入换行符来代替:
setlocal enableDelayedExpansion
set "str=45:abc"
echo !str::=^
!
--输出--
45
abc
最后一个! 上方的空行很关键。
我不知道为什么,但是当上面的输出通过管道传输到命令时,只保留了第一行。因此,可以将输出通过管道传输到与任何行匹配的 FINDSTR,并将结果定向到文件,然后可以使用 SET /P 将其读入变量。
在使用 SET /P 之前必须删除第二行,因为 SET /P 不能将 <LF> 识别为行终止符 - 它只能识别 <CR><LF>。
这是一个完整的解决方案:
@echo off
setlocal enableDelayedExpansion
set "str=45:abc"
echo(!str::=^
!|findstr "^" >test.tmp
<test.tmp set /p "var1="
del test.tmp
set "var2=!str:*:=!"
echo var1=!var1! var2=!var2!
更新
我相信我已经大致弄清楚为什么第二行从输出中被剥离了:)
这与 Windows cmd.exe 处理管道的方式有关,每一端都由一个新的 CMD.EXE 线程处理。请参阅 Why does delayed expansion fail when inside a piped block of code? 了解相关问题,jeb 给出了很好的回答。
只看管道命令的左侧,我相信它(在内存中)被解析成一个看起来像这样的语句
C:\Windows\system32\cmd.exe /S /D /c" echo {delayedExpansionExpression}"
我用{delayedExpansionExpression}表示尚未发生的多行查找替换扩展。
接下来,我认为变量表达式实际上被扩展并且通过搜索和替换将行分成两部分:
C:\Windows\system32\cmd.exe /S /D /c" echo 43
abc"
只有这样才能执行命令,并且按照正常的 cmd.exe 规则,命令在换行处结束。带引号的命令字符串缺少结束引号,但解析器并不关心这一点。
我仍然感到困惑的部分是abc" 发生了什么?我原以为会尝试执行它,导致错误消息像 'abc"' is not Recognized as an internal or external command, operable program or batch file。 但它似乎只是迷失在了以太中。
注意 - jeb 的第三条评论解释了原因:)
没有 FOR 的安全版本
我的原始解决方案不适用于this & that:cats & dogs 之类的字符串。这是一个没有 FOR 的变体,它几乎适用于任何字符串,除了字符串长度限制和尾随控制字符将从前导部分剥离。
@echo off
setlocal enableDelayedExpansion
set "str=this & that:cats & dogs"
set ^"str2=!str::=^
!^"
cmd /v:on /c echo ^^!str2^^!|findstr /v "$" >test.tmp
<test.tmp set /p "var1="
del test.tmp
set "var2=!str:*:=!"
echo var1=!var1! var2=!var2!
我将扩展延迟到新的 CMD 线程,并且我使用了一个 FINDSTR 正则表达式,$ 只匹配以 <cr> 结尾的行。第一行没有,第二行有。 /v 选项反转结果。
【讨论】:
{delayedExpansionExpression} 已经在 cmd /S/D/c 启动时发生`,因为在新的 cmd 上下文中,延迟扩展被禁用(有规则或错误,延迟扩展是在批处理上下文中完成的,而它只有一行不带括号)。我用%%cmdcmdline%% 对其进行了测试。第二部分的剥离是通过以下规则完成的
cmd /S/D/c 语句,然后扩展语句,然后在新线程中执行。我也知道cmd /S/D/c... 将在第一个换行符处停止,因此只有 43 被传送到 FINDSTR。但是为什么尝试执行abc" 没有错误呢? abc"去哪儿了?
abc" 被未转义的换行符hard从行中删除。与echo 123%LF%abc 中的相同。其余部分在百分比扩展之后,但在特殊字符阶段开始之前由解析器删除
是的,我知道这是一个非常古老的话题,但我刚刚发现它,我无法抗拒发布我的解决方案的诱惑:
@echo off
setlocal
set "str=45:abc"
set "var1=%str::=" & set "var2=%"
echo var1="%var1%" var2="%var2%"
您可以阅读此方法的完整详细信息here。
【讨论】:
这是一个没有讨厌技巧的解决方案
REM accepts userID@host
setlocal enableDelayedExpansion
set "str=%1"
set "host=%str:*@=%"
for /F "tokens=1 delims=@" %%F IN ("%str%") do set "user=%%F"
echo user@host = %user%@%host%
endlocal
【讨论】:
鉴于人们在这里发布了各种拆分变量的方法,我不妨发布我自己的方法,不仅允许一个而且几个拆分出来一个变量,用相同的符号表示,这在 REM 方法中是不可能的(我用了一段时间,谢谢@jeb)。
使用下面的方法,将第二行定义的字符串分成三部分:
setlocal EnableDelayedExpansion
set fulline=one/two/three or/more
set fulline=%fulline%//
REM above line prevents unexpected results when input string has less than two /
set line2=%fulline:*/=%
set line3=%line2:*/=%
set line1=!fulline:/%line2%=!
set line2=!line2:/%line3%=!
setlocal DisableDelayedExpansion
echo."%line1%"
echo."%line2%"
echo."%line3%"
输出:
"one"
"two"
"three or/more//"
我建议使用字符串的最后一个如此创建的分区作为剩余“安全”拆分字符的“bin”。
【讨论】: