Page 1 of 1
How to replace asterisks in a string?
Posted: 10 Nov 2016 09:19
by jfl
Last but not least, while debugging my script, I ironically hit a bug in my
debugging library In debug mode, the %RETURN% macro failed if the value of some returned variables contained a '?'.
The fix was simple: Replace the '?' by something else while going through the return magic, then change that back to a '?'. (Other problematic characters were already converted that way, and the performance is good.)
Now the root cause was in a for loop that processes every variables returned, and obviously the '*' character causes the same problem...
... But then I hit a wall: The string replacement syntax %var:OLD=NEW% does not work for asterisks in the OLD string. Contrary to question marks, they're interpreted as wild cards.
I tried escaping the asterisk with a caret, but this does not help.
Any idea on how to do that?
Re: How to replace asterisks in a string?
Posted: 10 Nov 2016 10:06
by Squashman
The asterisk will be treated as a wildcard if it is the first character in the old string.
Re: How to replace asterisks in a string?
Posted: 10 Nov 2016 10:27
by Jer
I don't know if this is kosher but it works for me. Test each character in the
string and re-assign the variable with all characters except the asterisk or insert
something else in its place, like "[asterisk]" if you need to know it was there or
need to have it out of the way temporarily and put it back later in the process.
Jerry
Re: How to replace asterisks in a string?
Posted: 10 Nov 2016 10:30
by dbenham
Easier said than done. It certainly can be done if you iterate every character in the string, but that is painful.
The asterisk behavior is documented in the SET help
Environment variable substitution has been enhanced as follows:
would expand the PATH environment variable, substituting each occurrence
of "str1" in the expanded result with "str2". "str2" can be the empty
string to effectively delete all occurrences of "str1" from the expanded
output.
"str1" can begin with an asterisk, in which case it will match
everything from the beginning of the expanded output to the first
occurrence of the remaining portion of str1.
Unfortunately there is no way to escape the behavior.
Dave Benham
Re: How to replace asterisks in a string?
Posted: 10 Nov 2016 11:23
by jfl
dbenham wrote:Unfortunately there is no way to escape the behavior.
Got any idea then: Using for /f "delims=*" to split the string.
I just verified that this:
Code: Select all
for /f "tokens=1* delims=*" %s in ('echo %STRING%') do @echo %s & echo %t
Extracts everything before the first asterix, then the tail afterwards, including all other asterisks.
The problem is then to efficiently loop while the tail is not empty, with trick cases like strings with consecutive asterisks, or some on the sides.
Re: How to replace asterisks in a string?
Posted: 10 Nov 2016 11:34
by Squashman
jfl wrote:dbenham wrote:Unfortunately there is no way to escape the behavior.
Got any idea then: Using for /f "delims=*" to split the string.
I just verified that this:
Code: Select all
for /f "tokens=1* delims=*" %s in ('echo %STRING%') do @echo %s & echo %t
Extracts everything before the first asterix, then the tail afterwards, including all other asterisks.
The problem is then to efficiently loop while the tail is not empty, with trick cases like strings with consecutive asterisks, or some on the sides.
But what happens when the asterisk is the first characters in the string?
Re: How to replace asterisks in a string?
Posted: 10 Nov 2016 13:32
by dbenham
Replacing = and * within variables is an known problem that has already been discussed extensively:
viewtopic.php?f=3&t=1485Another issue is variable find/replace ignores case.
These are some of the issues that led me to develop some utilities a number of years ago:
:replStr - Pure batch function to find/replace with fewer restrictions.
REPLVAR.BAT - Hybrid JScript/batch utility to do sophisticated find/replace operations using regular expressions.
I am not maintaining either of the above any more.
However, I have actively been working on an update to JREPL.BAT that will allow find/replace operations on variables. It will be much like REPLVAR.BAT, except it will be more robust, and have more sophisticated capabilities. I hope to post the JREPL.BAT update within 1 week.
Dave Benham
Re: How to replace asterisks in a string?
Posted: 10 Nov 2016 15:13
by jeb
It's hard to replace an asterix with pure batch, but it's possible, you can find it somewhere at dostips.
But I suppose it's the wrong way, perhaps it's easier to modify your functions to use FOR /F instead of "normal" FOR loops.
Then you don't need any find/replace manipulations at all.
Re: How to replace asterisks in a string?
Posted: 12 Nov 2016 20:45
by Jer
I got this idea from Aacini on a question about reducing code inside a for loop.
This cuts out the asterisks--a little less painful than iterating through every character
Code: Select all
@Echo Off
setlocal EnableDelayedExpansion
Set "var1=no asterisk in original string"
Set "var2=two ** words"
Set "var3=two ** words*"
Set "var4=t*wo ** words*"
Set "var5=* tw*o ** w*ords*"
Set "var6=* two *** words* *"
Set "var7=**no*spaces*in*string**"
Set "var8=**** * **** *"
Set "var9=*The *quick *brown *fox *jumped *over *the *lazy dog.*
Set "var10=** ** ***one*** more****** **test *to tame* the *asterisk.*** ** **"
For /L %%n In (1,1,10) Do (
Call :myfunc "!var%%n!" var
Echo before: !var%%n! after: !var!
)
endlocal & GoTo:eof
:myfunc
setlocal EnableDelayedExpansion
rem remove all asterisks from string
Set "string=%~1"
Set "str="
:loop
For /F "tokens=1,2* delims=*" %%x In ("%string%") Do (
Set "str=!str!%%x%%y"
Set "string=%%z"
)
If not "%string%"=="" GoTo:loop
endlocal & set "%~2=%str%"
Re: How to replace asterisks in a string?
Posted: 13 Nov 2016 06:32
by npocmaka_
Re: How to replace asterisks in a string?
Posted: 13 Nov 2016 12:50
by jfl
dbenham wrote:Replacing = and * within variables is an known problem that has already been discussed extensively:
viewtopic.php?f=3&t=1485
Thanks Dave for the reference. I started from the code there, then eventually found an even better way:
The idea is based on the observation that even though it's not possible to do %VAR:*=replacement% to replace asterisks, it IS possible to do %VAR:**=% to remove everything up to and including the first asterisk. Then, using strlen on the tail, it is possible to process the string and replace all asterisks.
Pro: This method is faster than the one in the topic you mentioned, because it uses fewer calls to strlen for each split.
Con: It works for several other tricky characters, but not for '='.
Code: Select all
:ReplaceStars STRVAR REPLACEMENT RETVAR
setlocal EnableDelayedExpansion
set "STRING=!%~1!"
set "REPL=%~2"
set "RESULT="
if defined STRING (
call :strlen STRING SLEN
:ReplaceStars.again
set "TAIL=!STRING:**=!"
call :strlen TAIL TLEN
if !TLEN!==!SLEN! ( :# No more asterisks
set "RESULT=!RESULT!!TAIL!"
) else ( :# Reached an asterisk
set /a "HLEN=SLEN-TLEN-1"
for %%h in (!HLEN!) do set "RESULT=!RESULT!!STRING:~0,%%h!!REPL!"
if defined TAIL (
set "STRING=!TAIL!"
set "SLEN=!TLEN!"
goto :ReplaceStars.again
)
)
)
endlocal & set "%~3=%RESULT%"
exit /b 0
Test result:
Code: Select all
#C:\JFL\SRC\Batch>set STRING
STRING=@||&&(())<<>>^^,,;; %%!!**??[[]]==~~''""%CD%_!CD!"
C:\JFL\SRC\Batch>ReplaceStar.bat STRING
"@||&&(())<<>>^^,,;; %%!![star][star]??[[]]==~~''""%CD%_!CD!""
C:\JFL\SRC\Batch>
Re: How to replace asterisks in a string?
Posted: 13 Nov 2016 12:57
by jfl
jeb wrote:perhaps it's easier to modify your functions to use FOR /F instead of "normal" FOR loops.
I must be missing something very basic, but for /f loops loop on file contents, not on file names. So how can you loop on multiple strings this way?
Code: Select all
C:\JFL\Temp>for %s in (A B C) do @echo %s
A
B
C
C:\JFL\Temp>for /f %s in (A B C) do @echo %s
The system cannot find the file A.
C:\JFL\Temp>for /f %s in ("A" "B" "C") do @echo %s
A"
C:\JFL\Temp>for /f %s in ("A B C") do @echo %s
A
C:\JFL\Temp>
Re: How to replace asterisks in a string?
Posted: 14 Nov 2016 00:38
by jeb
If you have already a string list you could use the line feed trick with FOR/F.
Code: Select all
set "str=A B C D"
(SET LF=^
%=EMPTY=%
)
for %%L IN ("!LF!") do set "str="!str: =%%L!"" # Replace all spaces with a linefeed
for /f "delims=" %%s in ("!str!") do @echo %%~s
output wrote:A
B
C
D
In this case I also enclose each entry into quotes, so you can handle empty entries and also entries beginning with the EOL character (;)
Re: How to replace asterisks in a string?
Posted: 14 Nov 2016 09:17
by Aacini
Simpler!
Code: Select all
@echo off
setlocal EnableDelayedExpansion
set "str=A B C D"
for /F "delims=" %%s in (^"!str: ^=^
%= Replace spaces with linefeeds =%
!^") do (
echo %%~s
)
Antonio
Re: How to replace asterisks in a string?
Posted: 14 Nov 2016 14:29
by jfl
jeb wrote:If you have already a string list you could use the line feed trick with FOR/F.
...
Great idea, thanks a lot!
It was hard to make it work in my complex %RETURN% macro, but worthwhile. I've just updated that macro in my debugging
library to use this, and it now works well with the most tricky strings I can think of, including with lots of * and ?. (And that with both expansion on or off).
FYI the overhead of using my %FUNCTION% / %UPVAR% / %RETURN% macros is significant. For example using them for the :strlen routine doubles its duration. (This is why I have two versions of :strlen, one with these macros and one without it for use in other low level routines.)
But I find that the advantages of using these macros far outweight that performance penalty:
- Tricky strings are returned correctly, without having to care about whether the caller and callee have expansion enabled or not.
- Adding more returned variables is a breeze: You can have multiple %RETURN% instructions in a routine, and none of them needs to change.
- In case of a problem, running the same script again in debug mode outputs a very readable trace of all instrumented function calls and all their returned variables.