Looping for every character in variable string BATCH
Asked Answered
C

4

7

I'm trying to loop through every character in a string . I only however know how to loop for every word in a string ussing the following:

(set /P MYTEXT=)<C:\MYTEXTFILE.txt

set MYEXAMPLE=%MYTEXT%
for %%x in (%MYEXAMPLE%) do (
ECHO DO SOMTHING
)

How can I configure it to work per character rather then per word?

Chalcedony answered 21/2, 2013 at 14:31 Comment(0)
P
4

AFAIK, FOR cannot do a character-wise iteration - A possible workaround is to build a loop like this:

@ECHO OFF
:: string terminator: chose something that won't show up in the input file
SET strterm=___ENDOFSTRING___
:: read first line of input file
SET /P mytext=<C:\MYTEXTFILE.txt
:: add string terminator to input
SET tmp=%mytext%%strterm%
:loop
:: get first character from input
SET char=%tmp:~0,1%
:: remove first character from input
SET tmp=%tmp:~1%
:: do something with %char%, e.g. simply print it out
ECHO char: %char%
:: repeat until only the string terminator is left
IF NOT "%tmp%" == "%strterm%" GOTO loop

Note: The question title states that you want to loop over "every character in variable string", which suggests the input file only contains a single line, because the command (set /P MYTEXT=)<C:\MYTEXTFILE.txt will only read the first line of C:\MYTEXTFILE.txt. If you want to loop over all lines in a file instead, the solution is a bit more complicated and I suggest you open another question for that.

Perreira answered 21/2, 2013 at 18:14 Comment(1)
Yeah that answers what I was thinking, which is that we cant iliterate for characters. this also explains why my loop works and where a posible solution exists in exiting the program loopChalcedony
T
7

This is a simple and direct way to loop through every character in a string:

@echo off
setlocal ENABLEDELAYEDEXPANSION

set /P mytext= < MYTEXTFILE.txt
echo Line is '%mytext%'

set pos=0
:NextChar
    echo Char %pos% is '!mytext:~%pos%,1!'
    set /a pos=pos+1
    if "!mytext:~%pos%,1!" NEQ "" goto NextChar
Twobyfour answered 29/7, 2016 at 16:26 Comment(7)
add setlocal enabledelayed expansion or the next comment will be "Doesn't work" ;) (and for heavens sake, remove the space between mytext and =)Dendroid
@Stephan, thanks. On my system "setlocal ENABLEDELAYEDEXPANSION" wasn't needed, but good point. I tested SET with and without the space and on my system it made no difference, and I found it more readable with the spaces. I guess it's been too many years since I've done bat for real.Twobyfour
Huh? The space definitively makes a difference in cmd (set var = value sets a variable %var<space>% to <space>value). What system (extensions) do you use?Dendroid
@Stephan, yeah, I know. But did you test if for this case, with /P option? Apparently the space is allowed there. So yeah, to a bat programmer the space looks wrong because of the usual case (as you point out), but in other languages I tend to add the space, so did it by current habit. Realized my 'mistake' was surprised it totally worked anyway, and left the space (until you reminded me how weird it must look). Give it a test. If space with /P causes a problem for you, please let me know which Windows version. Thanks.Twobyfour
be sure, I did test. Spaces do matter. I use Win10 (Version 10.0.10586) and Win7 Enterprise (Version 6.1.7601). (Both without any extensions). Also never heard anything like you say (not saying, you're lying. But if there is a "incompatibility", I'd like to know). (Note: set /a really doesn't care about spaces - but that's /a, not /p or plain set)Dendroid
@Stephan, Doh! You're right, thanks for setting this straight. Here's what happened (still had the cmd window open). While working it out on the command line, I typed the SET /P command line without any extra spaces. I created the bat file with extra spaces. When I tested the bat file it seemed to work. (You may already see where this is going.) I now see my environment had "mytext=test" (from cmdline testing) and "mytext =test" (from buggy script). The file contents hadn't changed so the output was correct, but just by chance. Never used /P option much, just thought that was handling it.Twobyfour
I already thought so. Glad that we were able to clarify this.Dendroid
P
4

AFAIK, FOR cannot do a character-wise iteration - A possible workaround is to build a loop like this:

@ECHO OFF
:: string terminator: chose something that won't show up in the input file
SET strterm=___ENDOFSTRING___
:: read first line of input file
SET /P mytext=<C:\MYTEXTFILE.txt
:: add string terminator to input
SET tmp=%mytext%%strterm%
:loop
:: get first character from input
SET char=%tmp:~0,1%
:: remove first character from input
SET tmp=%tmp:~1%
:: do something with %char%, e.g. simply print it out
ECHO char: %char%
:: repeat until only the string terminator is left
IF NOT "%tmp%" == "%strterm%" GOTO loop

Note: The question title states that you want to loop over "every character in variable string", which suggests the input file only contains a single line, because the command (set /P MYTEXT=)<C:\MYTEXTFILE.txt will only read the first line of C:\MYTEXTFILE.txt. If you want to loop over all lines in a file instead, the solution is a bit more complicated and I suggest you open another question for that.

Perreira answered 21/2, 2013 at 18:14 Comment(1)
Yeah that answers what I was thinking, which is that we cant iliterate for characters. this also explains why my loop works and where a posible solution exists in exiting the program loopChalcedony
J
0

The splitStr subroutine below:

  • will print out every character in the string one by one
  • can be safely called regardless of the state of delayed expansion
  • has no superslow goto loop
  • handles CR and LF
  • sets the errorLevel to the number of characters in the string
@echo off & setLocal enableExtensions disableDelayedExpansion
(call;) %= sets errorLevel to 0 =%

set "testStr=uncopyrightable"
call :splitStr testStr
if errorLevel 1 (
    >&2 echo(string is %errorLevel% char(s^) in length
) else (
    >&2 echo(empty string
    goto die
) %= if =%
goto end

:die
(call) %= sets errorLevel to 1 =%
:end
endLocal & goto :EOF

:splitStr string=
:: outputs string one character per line
setLocal disableDelayedExpansion
set "var=%1"

set "chrCount=0" & if defined var for /f "delims=" %%A in ('
    cmd /v:on /q /c for /l %%I in (0 1 8190^) do ^
    if "!%var%:~%%I,1!" neq "" (^
    echo(:^!%var%:~%%I^,1^!^) else exit 0
') do (
    set /a chrCount+=1
    echo%%A
) %= for /f =%

endLocal & exit /b %chrCount%
Johnson answered 28/12, 2017 at 18:56 Comment(1)
that's some voodoo dark magic right there... this runs... and echo's out each character in sequence with /n. NOTE: this only works if you hold your tongue just right. Any variations from the example will fail (see below). ALSO, I tried to modify the script to output rainbow colors for each character... and it is brittle... I couldn't get it to echo without newlines call :splitStr testStr REM works! ; call :splitStr "quotedtestStr" REM does NOT work! ; set quotedtestStr="uncopyrightable" REM does NOT work! ; call :splitStr %quotedtestStr% REM does NOT work! ;Closestool
S
0

CharByChar.bat:

@echo off
set sStr="MyString"
call :GetStrLen sStr iStrLen
echo The string %sStr% has %iStrLen% characters
for /L %%a in (1,1,%iStrLen%) do (
    call :Substring %sStr% %%a 1 sSingleChar
    echo !sSingleChar!
)
exit /b

Output:

C:\Projekte>CharByChar.bat
The string "MyString" has 10 characters
M
y
S
t
r
i
n
g

And here are the two functions which you can copy at the end of the batch file:

REM ### Substring(sLongString, iStart, iCharCount): sSubStrResult
REM ### =========================================================
REM ### Example:
REM ###   set sLongString=MyVeryLongString
REM ###   call :Substring %sLongString% 2 8 sSubStrResult
REM ###   echo %sSubStrResult%
REM ### Output: VeryLong
REM ### Michael Hutter / Dez 2023
:Substring
SET sLongStr=%1
SET iFirstChar=%2
SET iCharCount=%3
CALL SET sSubStr=%%sLongStr:~%iFirstChar%,%iCharCount%%%
(endlocal & set %4=%sSubStr%)
goto :eof

REM ### GetStrLen(sMyString): iStrLen
REM ### =============================
REM ### Example:
REM ###   set sLongString=MyVeryLongString
REM ###   call :GetStrLen sLongString iStrLen
REM ###   echo %sLongString% has %iStrLen% characters
REM ### Output: MyVeryLongString has 16 characters
REM ### Michael Hutter / Dez 2023
:GetStrLen
setlocal enabledelayedexpansion
:strLen_Loop
  if not "!%1:~%len%!"=="" set /A len+=1 & goto :strLen_Loop
(endlocal & set %2=%len%)
goto :eof
Sanfred answered 6/12, 2023 at 12:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.