How to replace asterisks in a string?

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
jfl
Posts: 226
Joined: 26 Oct 2012 06:40
Location: Saint Hilaire du Touvet, France
Contact:

How to replace asterisks in a string?

#1 Post by jfl » 10 Nov 2016 09:19

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?

Squashman
Expert
Posts: 4465
Joined: 23 Dec 2011 13:59

Re: How to replace asterisks in a string?

#2 Post by Squashman » 10 Nov 2016 10:06

The asterisk will be treated as a wildcard if it is the first character in the old string.

Jer
Posts: 177
Joined: 23 Nov 2014 17:13
Location: California USA

Re: How to replace asterisks in a string?

#3 Post by Jer » 10 Nov 2016 10:27

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

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: How to replace asterisks in a string?

#4 Post by dbenham » 10 Nov 2016 10:30

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:

    %PATH:str1=str2%

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

jfl
Posts: 226
Joined: 26 Oct 2012 06:40
Location: Saint Hilaire du Touvet, France
Contact:

Re: How to replace asterisks in a string?

#5 Post by jfl » 10 Nov 2016 11:23

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.

Squashman
Expert
Posts: 4465
Joined: 23 Dec 2011 13:59

Re: How to replace asterisks in a string?

#6 Post by Squashman » 10 Nov 2016 11:34

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? :twisted:

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: How to replace asterisks in a string?

#7 Post by dbenham » 10 Nov 2016 13:32

Replacing = and * within variables is an known problem that has already been discussed extensively: viewtopic.php?f=3&t=1485

Another 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

jeb
Expert
Posts: 1041
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Re: How to replace asterisks in a string?

#8 Post by jeb » 10 Nov 2016 15:13

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.

Jer
Posts: 177
Joined: 23 Nov 2014 17:13
Location: California USA

Re: How to replace asterisks in a string?

#9 Post by Jer » 12 Nov 2016 20:45

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%"

npocmaka_
Posts: 512
Joined: 24 Jun 2013 17:10
Location: Bulgaria
Contact:

Re: How to replace asterisks in a string?

#10 Post by npocmaka_ » 13 Nov 2016 06:32


jfl
Posts: 226
Joined: 26 Oct 2012 06:40
Location: Saint Hilaire du Touvet, France
Contact:

Re: How to replace asterisks in a string?

#11 Post by jfl » 13 Nov 2016 12:50

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>

jfl
Posts: 226
Joined: 26 Oct 2012 06:40
Location: Saint Hilaire du Touvet, France
Contact:

Re: How to replace asterisks in a string?

#12 Post by jfl » 13 Nov 2016 12:57

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>


jeb
Expert
Posts: 1041
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Re: How to replace asterisks in a string?

#13 Post by jeb » 14 Nov 2016 00:38

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 (;)

Aacini
Expert
Posts: 1885
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: How to replace asterisks in a string?

#14 Post by Aacini » 14 Nov 2016 09:17

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

jfl
Posts: 226
Joined: 26 Oct 2012 06:40
Location: Saint Hilaire du Touvet, France
Contact:

Re: How to replace asterisks in a string?

#15 Post by jfl » 14 Nov 2016 14:29

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.

Post Reply