How to loop through parts of a string separated by semicolon?

Microsoft Windows
Post Reply
User avatar
MigrationUser
Posts: 336
Joined: 2021-Jul-12, 1:37 pm
Contact:

How to loop through parts of a string separated by semicolon?

Post by MigrationUser »

01 Apr 2010 19:20
anandr

Hi all

I have a string made of many parts separated by ";".
I need to loop through all the parts.
I tried FOR loop, but it returns either whole string (solution #1) or only first part (solution #2).
Solution #3 works better, but total number of parts is unknown.
I came to the solution #4. It works the way I need.
Is it possible to have the same result with just FOR loop without GOTO?

Code: Select all

@Echo off

set text=p1;p2;p3;p4;p5;p6;p7;p8;p9;p10;p11;p12

echo.
set text

echo.
Echo -=[ Solution #1 ]=-

for /f "tokens=* delims=;" %%i in ("%text%") do (
    Echo %%i
    Echo %%k
)

echo.
Echo -=[ Solution #2 ]=-

for /f "delims=;" %%i in ("%text%") do (
    Echo %%i
    Echo %%k
)

echo.
Echo -=[ Solution #3 ]=-

for /f "tokens=1,2,* delims=;" %%i in ("%text%") do (
    Echo %%i
    Echo %%j
    Echo %%k
    Echo %%l
)

echo.
echo -=[ Solution #4 ]=-

set TN=1
:LL01
set part1=
for /f "tokens=%TN% delims=;" %%i in ("%text%") do (
    set part1=%%i
)
:: double quotes are important here, because string parts may contain spaces!
if "%part1%" neq "" (
    Echo %part1%
    SET /A TN+=1
    goto LL01
)
The code above produces this output (tested on WinXP SP3):
text=p1;p2;p3;p4;p5;p6;p7;p8;p9;p10;p11;p12

-=[ Solution #1 ]=-
p1;p2;p3;p4;p5;p6;p7;p8;p9;p10;p11;p12
%k

-=[ Solution #2 ]=-
p1
%k

-=[ Solution #3 ]=-
p1
p2
p3;p4;p5;p6;p7;p8;p9;p10;p11;p12
%l

-=[ Solution #4 ]=-
p1
p2
p3
p4
p5
p6
p7
p8
p9
p10
p11
p12
Everything will be OK at the end. If it is not OK -- it is not the end.

----------------------------

#2 01 Apr 2010 20:38
RG


How about this?

Code: Select all

@Echo off
set text=p1;p2;p3;p4;p5;p6;p7;p8;p9;p10;p11;p12

echo.
set text

echo.
Echo -=[ Solution #1 ]=-
call :parse "%text%"
pause
goto :eof

:Parse
for /f "tokens=1* delims=;" %%i in ("%~1") do (
    echo %%i
    call :Parse "%%j"
)
goto :eof
Let us know if you need an explanation.

Windows Shell Scripting and InstallShield

----------------------------

#3 02 Apr 2010 11:12
anandr


Thanks, RG. Your solution is definitely more elegant and easy to understand.

By the way, my solution #4 never returns more than 30 parts.
Is there a limit for number of tokens in FOR command?

After I played a little with the RG's code
I become curious what are the limitations for this method.
Here is two most important things I found:
1) Recursion level is limited by stack size
2) Command prompt (Cmd. exe) command-line string limitation
On computers running Microsoft Windows XP or later,
the maximum length of the string that you can use at the command prompt is 8191 characters.
On computers running Microsoft Windows 2000 or Windows NT 4.0,
the maximum length of the string that you can use at the command prompt is 2047 characters.
http://support.microsoft.com/kb/830473

Does anybody have an idea how to overcome the first limit?

The two scenarios can be illustrated with this script (it is basically RG's code with some make-up):

tester.cmd

Code: Select all

@Echo off
setlocal enabledelayedexpansion
if [%1] EQU [] goto PrintUsage

:: I use second argument to make %text% variable longer
set PartPrefix=%~2

echo Testing string composed of %1 parts. Prefix="%PartPrefix%"

:: Initializing %text% variable
FOR /L %%i IN (0,1,%1) DO set text=!Text!;%PartPrefix%%%i

call :parse "%text%"
echo Testing done
goto TheVeryEnd

:Parse
for /f "tokens=1* delims=;" %%i in ("%~1") do (
    rem echo %%i
    call :Parse "%%j"
)
goto TheVeryEnd

:PrintUsage
echo.
echo Usage:    %~n0    ^<NumberOfParts^> [^<PartPrefix^>]
goto TheVeryEnd

:TheVeryEnd
endlocal
Here is what I get:
C:\ProgramFiles\batch\recursion_test>Tester.cmd 500
Testing string composed of 500 parts. Prefix=""
Testing done

C:\ProgramFiles\batch\recursion_test>Tester.cmd 800
Testing string composed of 800 parts. Prefix=""
****** B A T C H R E C U R S I O N exceeds STACK limits ******
Recursion Count=753, Stack Usage=90 percent
****** B A T C H PROCESSING IS A B O R T E D ******

C:\ProgramFiles\batch\recursion_test>Tester.cmd 500 EachPartIsNowLonger
Testing string composed of 500 parts. Prefix="EachPartIsNowLonger"
The input line is too long.
:Parse
was unexpected at this time.

C:\ProgramFiles\batch\recursion_test>
Last edited by anandr (02 Apr 2010 11:17)

Everything will be OK at the end. If it is not OK -- it is not the end.

----------------------------

#4 02 Apr 2010 17:46
RG


For practical purposes the limits for tokens in FOR loops are a-z and A-Z (case sensitive).
Check out the following link if you need more than that.
http://www.robvanderwoude.com/clevertri ... stExtended
Don't know about increasing the stack size. I will look later.

Windows Shell Scripting and InstallShield

----------------------------

#5 02 Apr 2010 22:51
RG


Well one needs to be careful with GOTO, but you might consider something like this:

Code: Select all

Echo off
set text=p1;p2;p3;p4;p5;p6;p7;p8;p9;p10;p11;p12

REM Replace ; with space and don't use quotes because we want multiple arguments instead of 1
set text=%text:;= %
call :parse %text%
pause
goto :eof

:Parse
echo.%1
shift
if not "%1"=="" GOTO :Parse
goto :eof
That would eliminate your stack problem, but you still may have the line length limitation.
Perhaps instead of putting all of your input into a variable, you should write it to a temp file. As you put it in there, break it up so that each "part" is a single line in your temp file and then just iterate through the temp file. Do something like this at the beginning of your program to specify a text file with the same name as you bat file in the temp folder and delete an existing leftover file (in case you aborted program).

Code: Select all

set TempFile=%TEMP%\%~n0.txt
if exist %TempFile% del /q %TempFile%
Then do this at the end of your program to clean up.

Code: Select all

if exist %TempFile% del /q %TempFile%
best of luck!

Windows Shell Scripting and InstallShield

----------------------------

#6 15 Apr 2010 15:52
SmartGenius


I Think that the Semicolon has treated by the CMD as a Space, just call a Function in your code and return the parameter you want, like in this example to get a Random Path Dir

Code: Select all

@Echo off

:Int
Set /a "Rnd=(%Random% %% 4)"
Call :GetDir %Path%
Echo. %Target%
Pause
Exit

:GetDir
For /l %%a in (1,1,%Rnd%) do (Shift)
Set "Target=%1"
Goto :Eof
Greets !

----------------------------

#7 16 Apr 2010 23:31
carlos


Check this solution

Code: Select all

@echo off
setlocal enableextensions
set "text=p1;p2;p3;p4;p5;p6;p7;p8;p9;p10;p11;p12"
call :iterate text
pause
goto :eof

:iterate:
setlocal enableextensions enabledelayedexpansion
:iterate_:
for /f "tokens=1,* delims=;" %%a in (
"!%1!") do set "%1=%%b" & echo.%%a
if "!%1!" neq "" goto :iterate_:
goto :eof
Post Reply