Page 1 of 3

How to replace "=","*", ":" in a variable

Posted: 25 Nov 2010 06:07
by jeb
Hi,

amel27 introduced the problem of replacing "=", ":", "*" or "~" signs.

It is obvious that replacing an equal is tricky, because it is also the delim for the part of original to replace text.
So this doesn't work ( try to replace "=" with "#")

Code: Select all

set "var=abc=def"
echo %var:==#%


The other characters have problems if they are the first character, because of their special meaning.

So the qeustion is, how to resolve it :?:
A good solution shouldn't iterate through each character.

jeb

Re: How to replace "=","*", ":" in a variable

Posted: 25 Nov 2010 08:22
by orange_batch
A quick adaptation of my Get Last Token code.

Args: "string" "char(s) to find" "char(s) to place"

Code: Select all

@echo off&setlocal enabledelayedexpansion

call :l_replace "yellow banana=black cat=rainbow = noodle=beer mug" "=" "#"
echo:!str!
call :l_replace "yellow banana:black cat:rainbow : noodle:beer mug" ":" "#"
echo:!str!
call :l_replace "yellow banana*black cat*rainbow * noodle*beer mug" "*" "#"
echo:!str!
call :l_replace "yellow banana~black cat~rainbow ~ noodle~beer mug" "~" "#"
echo:!str!

call :l_replace "yellow banana=:*~black cat~*:=rainbow =~*~:*=* noodle~beer mug" "=:*~" "#---#"
echo:!str!

call :l_replace "yellow banana=" "=:*~" "#---#"
echo:!str!
call :l_replace "=yellow banana" "=:*~" "#---#"
echo:!str!

pause
exit

:l_replace
set "str=x%~1x"
:l_replaceloop
for /f "delims=%~2 tokens=1*" %%x in ("!str!") do (
if "%%y"=="" set "str=!str:~1,-1!"&exit/b
set "str=%%x%~3%%y"
)
goto l_replaceloop

Iterates by tokens, so it's much faster than char-by-char iteration.

Delayed expansion isn't even necessary. (Remove setlocal and change !str! to %str% is all.) But of course, you lose the ability to echo command characters outside of quotation marks.

(PS: The x's in set "str=x%~1x" is to allow single-token strings. Maybe optimizations are possible, but I need to sleep.)

Re: How to replace "=","*", ":" in a variable

Posted: 26 Nov 2010 16:07
by jeb
Good thought, I suppose this way could be successful.

But two problems.
1. As long as DelayedExpansion is active over a for loop, you got problems with exclamation marks and carets.

Code: Select all

set "Teststring=caret^ and !"

the "!" and the "^" are removed in the %%y expansion.

This can be easy solved with toggling the DelayedExpansion.


2. It fails on strings with more than one "char to find" combined.

Code: Select all

call :l_replace "===yellow banana == ====" "=" "#"

I don't know a good solution for this, perhaps with retrieving the strlen for each token, or something else.

jeb

Re: How to replace "=","*", ":" in a variable

Posted: 26 Nov 2010 18:04
by orange_batch
Yeah, I realized that problem. DOS, as usual, caters to a best-case-scenario.

So far, the most robust solution would let you choose between token-by-token and char-by-char.

Re: How to replace "=","*", ":" in a variable

Posted: 07 May 2011 00:28
by amel27
The problem of replacing "=" it is possible to connect to problem of associative arrays. One solution is using variable Name as KEY and Value as DATA. Only <LF> not supported in variable name. The idea consists in sequentially splitting a line into a variable NAME and VALUE by every "=" symbol. Then, all NAMES combined to string with new delimiter.

In addition, original string must not contains "!", because we can't get VALUE of variable with exclamations in NAME via Delayed Expansion. Exclamations (if exists) should be replace prior... Or probably, the problem has the another decision :?:


We should know the max possible amount of "=" in string (99 by default) and set it in FOR loop as upper bound. FOR-Loop is more quickly than GoTo-Loop.

Code: Select all

@Echo Off

Set "$var====%%yellow%% |banana| == ===="
Set "$rep=#"

Set $var
Call:EQ_Replace $var $rep
Set $var
Pause>Nul
Exit

:EQ_Replace  %VarString%  %VarReplacement%
::----------------------------------------
(SETLOCAL EnableDelayedExpansion
Set "$_=!%~1!|"& Set "$f=1"& Set "$v="
For /L %%i In (0,1,99) Do If Defined $f (
  For /F "Delims==" %%a In ('Set $_') Do (Set "$a=%%a"& Set "$b=!%%a!"
    Set "%%a="& Set "$_!$b!"2>Nul ||Set "$f="
    If %%i gtr 0 Set "$v=!$v!!$a:~2!!%~2!" ))
For /F "Delims=" %%a In ("!$v!") Do ENDLOCAL& Set "%~1=%%a"
)
GoTo:EOF
result:

Code: Select all

$var====%yellow% |banana| == ====
$var=###%yellow% |banana| ## ####

Re: How to replace "=","*", ":" in a variable

Posted: 18 May 2011 13:29
by jeb
Hi amel27,

the trick is impressive, I need some minutes to understand it.
I never thought about to misuse the SET command like you do. :D

The only problem seems to be, that it only works for the equal sign

jeb

Re: How to replace "=","*", ":" in a variable

Posted: 02 Jun 2011 06:37
by dbenham
amel27 wrote:Only <LF> not supported in variable name.

Is that a precise statement? How do you get = in a name? or are you ignoring the = case because that is what you are replacing? I've never figured a way to use = in a name.

Dave Benham

Re: How to replace "=","*", ":" in a variable

Posted: 02 Jun 2011 07:04
by jeb
amel27 wrote:Only <LF> not supported in variable name.

It's wrong, a variable name can contain a <LF>.

Code: Select all

set LF=^


rem ** Two lines empty
set XX!LF!YY=A!LF!B
set XX

It works...

dbenham wrote:How do you get = in a name?

Amel27 didn't try to get a equal sign into the name, that is the impressive trick :).
Using the equal sign as a delimiter, but unlike the DELIMS in the FOR /F statement, the SET only consumes a single sign, therefore this way of splitting works.

jeb

Re: How to replace "=","*", ":" in a variable

Posted: 02 Jun 2011 07:13
by dbenham
Thanks Jeb - That's good to know about <LF> in names.

I know he wasn't trying to get = in a variable name, otherwise his technique would not work. I was just trying to confirm that you can't under any circumstances put = in a variable name.

Based on your <LF> discovery, I believe the correct statement is "Only = not supported in a variable name"

Dave

Re: How to replace "=","*", ":" in a variable

Posted: 02 Jun 2011 07:31
by jeb
dbenham wrote:I was just trying to confirm that you can't under any circumstances put = in a variable name.


But there are equal signs in variable names :!: :)

Code: Select all

echo %=C:%

In the well known =C: =D: =exitcode =exitcodeAscii and =::

jeb

Re: How to replace "=","*", ":" in a variable

Posted: 02 Jun 2011 14:52
by dbenham
Troublemaker! :lol:

I must admit I forgot about them, though they are truely read only. We can't override their value by setting our own values like we can with DATE, TIME, ERRORLEVEL, RANDOM, etc.

Have you ever seen any official documentation for these obscure dynamic variables beginning with =? Or are they undocumented features :?:

I'm familiar with:
  • =C: - current directory of the C: drive (works for any drive latter that has been accessed by the cmd session)
  • =exitcode - most recent exit code expressed as HEX
  • =exitcodeAscii - most recent exit code expressed as an ASCII character as long decimal value is between 32 and 126

But what on earth is =:: :?:

Dave Benham

Re: How to replace "=","*", ":" in a variable

Posted: 07 Jun 2011 15:37
by jeb
dbenham wrote:But what on earth is =:: :?:


I didn't know, I never read something about it, I can only guess...

But I know how to access it :)

Code: Select all

setlocal DisableExtensions
echo %=::%


I didn't found a way to modify the value (always ::\ ), I tried pushd, cd and searched the registry.
I never saw another value.
But I suppose that it describes something of path's (only a guess).

jeb

Re: How to replace "=","*", ":" in a variable

Posted: 07 Jun 2011 23:01
by dbenham
jeb wrote:But I know how to access it :)

Code: Select all

setlocal DisableExtensions
echo %=::%

I didn't found a way to modify the value (always ::\ )

Did I get you :!: :?: Did you test :?: :lol:

Here is what I get on my machine from an interactive prompt:

Code: Select all

C:\test>echo %=::%
%=::%

This is what I get on my machine within a batch:

Code: Select all

echo %=::%
Results:

Code: Select all

:

The only way I see the ::\ value is through SET "" (interactive or batch):

Code: Select all

C:\test>set "" | findstr /b =:
=::=::\


It gets weirder than that. I think the values I am seeing via ECHO are related to the results of substring or replacement operations on an undefined variable. It doesn't seem to matter what characters I place before the :, it always echoes the characters appearing after the colon.

Code: Select all

@echo off
echo %=:hello world%
echo %!^^^&^<^>\/():after colon%
echo %undefined:~0,5%
echo %undefined:a=b%
Results:

Code: Select all

hello world
after colon
~0,5
a=b


I tried the above experiments with delayed expansion, with the same results. It doesn't seem like the =:: variable is real. So why does it show up with SET "" :?: :?

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

Returning to the original theme of this thread (sort of). I think I have another case where replacement (and substring) is problematic. I don't think it is possible to perform substring or replacement on a variable if the name ends with a colon (:) . This has an impact on the =C:, =D: etc. variables.

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

Just an observation - It seems very odd that normal dynamic variables (ERRORLEVEL, TIME, RANDOM etc) never show up in SET command, but do test as defined (IF DEFINED ERRORLEVEL is true). But the = dynamic variables do show up in SET "" command, but test as undefined (IF DEFINED =C: is false)

Dave Benham

Re: How to replace "=","*", ":" in a variable

Posted: 07 Jun 2011 23:07
by Ed Dyreen

When would u use such a variable, why would u need it ?
=C:, =D:

Re: How to replace "=","*", ":" in a variable

Posted: 08 Jun 2011 00:17
by jeb
dbenham wrote:Did I get you :!: :?: Did you test :?: :lol:

Here is what I get on my machine from an interactive prompt:
Code:
C:\test>echo %=::%
%=::%

This is what I get on my machine within a batch:
Code:
echo %=::%
Results:
Code:
:

You didn't get me :) , you didn't read carefully :D

The setlocal DisableExtensions is the key :!:

dbenham wrote:Just an observation - It seems very odd that normal dynamic variables (ERRORLEVEL, TIME, RANDOM etc) never show up in SET command, but do test as defined (IF DEFINED ERRORLEVEL is true). But the = dynamic variables do show up in SET "" command, but test as undefined (IF DEFINED =C: is false)


I didn't know if it is "defined", as I didn't know how to test it, in your case you only test if "C:" is defined, with echo on you will see
if defined C: (echo exist ) ELSE echo not defined


jeb