【问题标题】:How to split string in a variable without loosing spaces?如何在不丢失空格的情况下拆分变量中的字符串?
【发布时间】:2013-04-10 09:38:27
【问题描述】:

如何拆分字符串

"   This  is  a text  with  spaces    "

在变量“字符串”中 在不丢失空格的情况下放入文本部分?

set string="#   This  is  a text  with  spaces    #"

应该拆分成

"#   This"
"  is"
"  a"
" text"
"  with"
"  spaces    #"

使用For /F "delims= " ... 不起作用,因为它消除了所有空格。

是否有一个“简单”的解决方案,或者任何人都可以解释如何逐个字符地解析字符串 所以我可以将空格数到第一个字符,然后读取所有字符直到下一个空格并写入 计数的空格和读取的字符一起到一个新的/临时变量??

谢谢

【问题讨论】:

  • #s 与此事有什么关系?它们似乎是凭空出现的。
  • # 表示字符串的开头和结尾。但这并不重要,因为字符串也可以在开头和/或结尾包含这个 #。
  • 实际上,它非常重要 - 仍然没有解释您的输出。我会得出结论,您想在每个空间序列处开始一个新的子字符串;但这会使您的第一个子字符串# 和第二个`This;your second-last 空格`和最后一个`#` - 这与您发布的结果不同。
  • 前两个和后两个字符串必须分开处理。这使问题变得更加复杂。

标签: string parsing batch-file split space


【解决方案1】:

是的,我也不太了解#"    空格    #" 是什么让它保留了尾随空格,而所有其他元素保留了前面的空格而不是前面的空格?

哦,好吧,花在提问上的努力=花在回答上的努力。随心所欲。

@if (@a==@b) @end /*

:: batch portion

@echo off
setlocal

call :split "#   This  is  a text  with  spaces    #"
exit /b

:split <string>
cscript /nologo /e:jscript "%~f0" "%~1"
goto :EOF

:: JScript portion */
WSH.Echo(WSH.Arguments(0).match(/\s*\S+/g).join('\n'));

输出:

#
   This
  is
  a
 text
  with
  spaces
    #

更新

如果你想要第一个+第二个,倒数第二个+最终元素加入,修改上面脚本的JScript部分如下:

:: JScript portion */
var m = WSH.Arguments(0).match(/\s*\S+/g);
m[0] = m.shift() + m[0];
m[m.length - 2] += m.pop();
WSH.Echo(m.join('\n'));

输出:

#   This
  is
  a
 text
  with
  spaces    #

如果您希望将每个元素括在引号中,请将最后一行更改如下:

    WSH.Echo('"' + m.join('"\n"') + '"');

输出:

"#   This"
"  is"
"  a"
" text"
"  with"
"  spaces    #"

【讨论】:

  • " 个空格 #" 有尾随空格,因为第一个单词或最后一个单词两边都有空格。
  • 所以你希望它按照我的方式拆分,但是第一个 + 第二个和倒数第二个 + 最后一个元素加入了。好的。我会更新我的答案。
【解决方案2】:

我没有看到一个简单的批量解决方案,但当然,如果您可以考虑使用 powershell 或 javascript,您将使用更合适的工具集来处理字符串。

坚持批量要求,您可以逐个字符循环并使用以下内容“收集”您的单词:

@echo off
setlocal enabledelayedexpansion

set "string=   This  is  a text  with  spaces    "

set idx=0
set "word="
set "char="
set "lastchar= "
:loop
if "!string:~%idx%!" equ "" goto :eof
set char=!string:~%idx%,1!
if "%char%" equ " " (
    if "%lastchar%" neq " " (
        echo [%word%]
        set word=%char%
    ) else (
        set word=%word%%char%
    )
) else (
    set word=%word%%char%
)
set lastchar=%char%
set /a idx=%idx%+1
goto loop

此脚本使用批处理的子字符串功能!string:~%idx%,1 从字符串中获取单个字符,每次循环递增idx。然后,当前一个字符不是空格而当前字符是空格时,只需处理单词(本例中为echo)即可。

这样写:

[   This]
[  is]
[  a]
[ text]
[  with]
[  spaces]

请注意,我忽略了您在示例中使用的 #,因为我不明白它们的位置。

【讨论】:

    【解决方案3】:

    诀窍是用一个空格替换相邻的空格,其余的替换为任意字符。假设您的字符串不包含 #s 并且连续空格不超过 9 个,您可以试试这个

    set st=%st:         = ########%
    set st=%st:        = #######%
    set st=%st:       = ######%
    set st=%st:      = #####%
    set st=%st:     = ####%
    set st=%st:    = ###%
    set st=%st:   = ##%
    set st=%st:  = #%
    

    然后你可以用for /f 解析并用空格替换你的#s

    setlocal enabledelayedexpansion
    for /f %%a in ("%st%") do (
      set ss= %%a
      echo !ss:#= !
    )  
    

    请注意,括号块内的set 要求您启用延迟扩展并使用! 语法(请参阅HELP SET

    但是这种技术只会提取第一个子字符串。概括地说,您需要另一个技巧,即将空格替换为换行符,以便 for /f 将逐行循环

    请注意,为了获得换行符,您需要保留set 命令后的两个空行

    set nl=^
    
    
    rem continue two lines down....
    for /f %%a in ("%st: =!nl!%") do (
      set ss= %%a
      set ss=!ss:#= !
      echo [!ss!]
    )  
    

    【讨论】:

    • +1,只要连续空格的数量受到合理限制,答案就很好。我编写了一个更通用的基于搜索和替换的解决方案,它依赖于最少数量的 GOTO。见my answer
    【解决方案4】:

    试试这个:

    @echo off &setlocal enabledelayedexpansion
    set "string=#   This  is  a text  with  spaces    #"
    
    set string1=%string%
    for %%i in (%string%) do (
        set string1=!string1: %%i = "%%i" !
        set /a strings+=1
    )
    set string1=#"%string1:~1,-1%"#
    set string1=%string1:"= "%
    for %%i in (%string1%) do (
        set /a count+=1
        set string2=%%i
        set string2=!string2: "=!
        set string2=!string2:"=!
        if !count! equ 2 (
         set $s1=!$s1!!string2!
        )else if !count! equ %strings% (
            set /a count-=1
            call set $s!count!=%%$s!count!%%!string2!
            ) else set $s!count!=!string2!
    )
    for /f "tokens=1*delims==" %%i in ('set "$s"') do echo "%%j"    
    

    输出:

    "#   This"
    "  is"
    "  a"
    " text"
    "  with"
    "  spaces    #"
    

    【讨论】:

      【解决方案5】:

      如果我必须完成这个晦涩难懂的任务,我会使用像rojo's answer 这样的混合 JScript/批处理技术。但是,我会使用REPL.BAT utility that I have already written。假设我的 REPL.BAT 位于当前文件夹中,或者位于 PATH 中的某个位置,那么以下内容将起作用:

      @echo off
      setlocal enableDelayedExpansion
      set "string=#   This  is  a text  with  spaces    #"
      
      :: Build an "array" of text parts
      set cnt=0
      for /f delims^=^ eol^= %%A in ('repl "([^ ])(?= )" "$1\n" xs string') do (
        set /a cnt+=1
        set "string!cnt!=%%A"
      )
      
      :: Print the array values
      for /l %%N in (1 1 %cnt%) do echo string%%N=[!string%%N!]
      

      但如果我想要一个纯批处理解决方案,我会使用以下相当有效的方法:

      @echo off
      setlocal enableDelayedExpansion
      set "string=#   This  is  a text  with  spaces    #"
      
      :: Define LF to contain a single line feed character (0x0A)
      set LF=^
      
      
      :: Above 2 blank lines are critical - DO NOT REMOVE
      
      
      :: Insert a line feed before every space
      for %%n in ("!LF!") do set "string=!string: =%%~n !"
      
      :loop  Remove line feeds sandwiched by spaces
      for %%n in ("!LF!") do set "string2=!string: %%~n =  !"
      if "!string2!" neq "!string!" (
        set "string=!string2!"
        goto :loop
      )
      
      :: Build an "array" of text parts: FOR /F splits the string at line feeds
      set /a cnt=0
      for /f delims^=^ eol^= %%A in ("!string!") do (
        set /a cnt+=1
        set "string!cnt!=%%A"
      )
      
      :: Print out the array values
      for /l %%N in (1 1 %cnt%) do echo string%%N=[!string%%N!]
      

      上述两种解决方案都给出以下输出:

      string1=[#]
      string2=[   This]
      string3=[  is]
      string4=[  a]
      string5=[ text]
      string6=[  with]
      string7=[  spaces]
      string8=[    #]
      

      请注意,如果字符串包含 ! 由于延迟扩展,FOR 循环 %%A 扩展将损坏结果。这个限制可以通过额外的编码来消除。所有其他发布的使用 FOR 循环的解决方案都受到同样的限制。 (至少在我写这篇文章时他们做到了)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-05-29
        • 2014-06-11
        • 2023-03-15
        • 2020-04-09
        • 2015-08-31
        • 1970-01-01
        • 2013-11-12
        • 1970-01-01
        相关资源
        最近更新 更多