Before I begin, I assure you, this is not a duplicate. I have read multiple solutions for replacing a certain character in a string, but that is not specifically what I want to achieve. I know how to replace X in the string STUVWXYZ, but instead I want to replace the 5th letter with A. Example:
set p=5
set string=STUVWXYZ
set replacewith=A
How can I replace a character, defined in the position p, with a character defined in the variable replacewith? If this is not possible, is it possible to not use the replacewith variable, and replace the character with another fixed character?
Thanks
Yeah, just sandwich the replacement with substrings.
#echo off
setlocal enabledelayedexpansion
set p=5
set string=STUVWXYZ
set replacewith=A
:: get first %p% characters of string
set "left=!string:~0,%p%!"
:: remove %p%+1 characters for the right half
set /a r = p + 1
set "right=!string:~%r%!"
:: left + middle + right
set "string=%left%%replacewith%%right%"
echo %string%
If you want to do this more than once in a script, it might make sense to turn this into a subroutine like this:
#echo off
setlocal
set p=5
set string=STUVWXYZ
set replacewith=A
call :replace string %p% %replacewith%
echo %string%
goto :EOF
:replace <var_to_manipulate> <position> <replacement>
setlocal enabledelayedexpansion
set "string=!%~1!"
set "p=%~2"
set /a r=p+1
set "left=!string:~0,%p%!"
set "right=!string:~%r%!"
endlocal & set "%~1=%left%%~3%right%"
goto :EOF
Related
In a batch script, I need to split only the last string match in a variable in a loop until I no longer have the string match.
Input=Level1/Level2/Level3/Level4/LevelN
(where N can be any number)
Output:
Level1/LeveL2/Level3/Level4
Level1/LeveL2/Level3
Level1/Level2
Level1
I have tried the usual "for /f "delims=/"" loops, but they only output each split of the input variable on an individual line. Besides, the value of "N" can vary. So I can't set the number of tokens to a certain value.
Please help.
This site don't works that way. You must post some code and explain the problems you have with it. In this way you can understand the changes made to your own code. If you request us for code, any code, then you could receive one ("any code") like this:
#echo off
setlocal EnableDelayedExpansion
set "Input=Level1/Level2/Level3/Level4/LevelN"
:loop
set "Output="
set "part=%Input:/=" & set "Output=!Output!/!part!" & set "part=%"
set "Input=%Output:~1%"
if "%Input%" equ "~1" goto exitLoop
echo "%Input%"
goto loop
:exitLoop
Here is a nice recursive approach that makes use of the ~ modifiers, assuming that the input string is provided as a quoted ("") command line argument, which does not begin with /, does not contain two consecutive // and none of the characters ", \, *, ?, <, >:
#echo off
rem // Store argument in variable:
set "INPUT=%~1"
if not defined INPUT exit /B
rem /* Precede with `\` and replace each `/` by `\`, so the resulting string appears to
rem be an absolute path, which can be split by `~` modifiers of `for` variables;
rem the inner `for` loop resolves the split path and removes any `\.` suffix: */
for %%I in ("\%INPUT:/=\%") do for %%J in ("%%~pI.") do set "REST=%%~pnxJ"
rem // Revert replacement of every `/` by `\` and remove the previously preceded `\`:
set "REST=%REST:\=/%"
set "REST=%REST:*/=%"
rem // If there is a string left, output it and call this script recursively:
if defined REST (
setlocal EnableDelayedExpansion
echo(!REST!
endlocal
call "%~f0" "%REST%"
)
A more classical approach, just for fun...
#echo off
setlocal EnableDelayedExpansion
set "Input=Level1/Level2/Level3/Level4/LevelN"
rem Split the string, join it again and store partial results
set "aux="
set "n=0"
for %%a in ("%Input:/=" "%") do (
set "aux=!aux!/%%~a"
set /A n+=1
set "out[!n!]=!aux:~1!"
)
rem Show results in reverse order
set /A n-=1
for /L %%i in (%n%,-1,1) do echo !out[%%i]!
This is easily done with a regex in PowerShell. Since the regex is greedy be default, it will get everything up to the last SOLIDUS and store it in the $1 group.
SET "S=Level1/Level2/Level3/Level4/LevelN"
FOR /F "delims=" %%a IN ('powershell -NoL -NoP "'%S%' -replace '(.*/).*', '$1'"') DO (
SET "RESULT=%%a"
)
ECHO RESULT is set to %RESULT%
Revision:
This outputs all groups and not just the last one.
SET "S=Level1/Level2/Level3/Level4/LevelN"
FOR /F "delims=" %%a IN ('powershell -NoL -NoP -Command ^
"$a = '%S%'.Split('/'); " ^
"for ($i = $a.Count - 2; $i -ge 0; $i--) { $a[0..$i] -join '/' }"') DO (
ECHO %%a
)
I have the following string in a batch file script:
ABCE#$1 TroubleMaker[FFFFF A=MyCountry US=1 CA=1 JP=1 EU=1
and it's stored in _var,when I do
set _var=%_var:* A=% - it cuts all the characters before " A" (including the 'A') and i'm left with =MyCountry US=1 CA=1 JP=1 EU=1
how can I change the set command to cut also the = mark from the string?
tried set _var=%_var:*==% - didn't work.
Thanks.
#ECHO OFF
SETLOCAL
SET "string=ABCE#$1 TroubleMaker[FFFFF A=MyCountry US=1 CA=1 JP=1 EU=1"
FOR /f "tokens=1*delims==" %%s IN ("%string%") DO SET "string=%%t"
ECHO "%string%"
GOTO :EOF
This assumes that you want to delete up to and including the initial =
The = disturbs the substring replacement syntax, because it contains a = on its own.
You could go for the following work-around:
set _var=%_var:* A=%
set _var=%_var:~1%
The second line constitutes the substring expansion syntax (type set /? for details), which splits off the first character, that is supposed to be a =.
This of course works only if the = immediately follows the A substring.
You can check whether the first character is = before removing it, like:
set _var=%_var:* A=%
if "%_var:~,1%"=="=" set _var=%_var:~1%
If you just want to search for the (first) = character and to ignore the A substring, you could establish a loop structure like this:
:LOOP
if "%_var%"=="" goto :END
if "%_var:~,1%"=="=" (
set _var=%_var:~1%
goto :END
) else (
set _var=%_var:~1%
goto :LOOP
)
:END
This cuts off the first character and checks whether it is a =. If it is, the remaining string is stored in _var and the loop is left; if not, the loop continues checking the next character. The first line is inserted to not hang in case the string does not contain a = character.
I have a text file that is one long string like this:
ISA*00*GARBAGE~ST*TEST*TEST~CLP*TEST~ST*TEST*TEST~CLP*TEST~ST*TEST*TEST~CLP*TEST~GE*GARBAGE*~
And I need it to look like this:
~ST*TEST*TEST~CLP*TEST
~ST*TEST*TEST~CLP*TEST
~ST*TEST*TEST~CLP*TEST
I first tried to add a line at every ~ST to split the string up, but I can't for the life of me make this happen. I have tried various scripts, but I thought a find/replace script would work best.
#echo off
setlocal enabledelayedexpansion
set INTEXTFILE=test.txt
set OUTTEXTFILE=test_out.txt
set SEARCHTEXT=~ST
set REPLACETEXT=~ST
for /f "tokens=1,* delims=~" %%A in ( '"type %INTEXTFILE%"') do (
SET string=%%A
SET modified=!string:%SEARCHTEXT%=%REPLACETEXT%!
echo !modified! >> %OUTTEXTFILE%
)
del %INTEXTFILE%
rename %OUTTEXTFILE% %INTEXTFILE%
Found here How to replace substrings in windows batch file
But I'm stuck because (1) the special character ~ makes the code not work at all. It gives me this result:
string:~ST=~ST
The code does nothing at all if using quotes around "~ST". And (2) I can't figure out how to add a line break before ~ST.
The final task for this would be to delete the ISA*00*blahblahblah and ~GE*blahblahblah lines after all splits have been performed. But I am stuck on the splitting at ~ST part.
Any suggestions?
#echo off
setlocal EnableDelayedExpansion
rem Set next variable to the number of "~" chars that delimit the wanted fields, or more
set "maxTokens=7"
rem Define the delimiters that starts a new field
set "delims=/ST/GE/"
for /F "delims=" %%a in (test.txt) do (
set "line=%%a"
set "field="
rem Process up to maxTokens per line;
rem this is a trick to avoid a call to a subroutine that have a goto loop
for /L %%i in (0,1,%maxTokens%) do if defined line (
for /F "tokens=1* delims=~" %%b in ("!line!") do (
rem Get the first token in the line separated by "~" delimiter
set "token=%%b"
rem ... and update the rest of the line
set "line=%%c"
rem Get the first two chars after "~" token like "ST", "CL" or "GE";
rem if they are "ST" or "GE":
for %%d in ("!token:~0,2!") do if "!delims:/%%~d/=!" neq "%delims%" (
rem Start a new field: show previous one, if any
if defined field echo !field!
if "%%~d" equ "ST" (
set "field=~%%b"
) else (
rem It is "GE": cancel rest of line
set "line="
)
) else (
rem It is "CL" token: join it to current field, if any
if defined field set "field=!field!~%%b"
)
)
)
)
Input:
ISA*00*GARBAGE~ST*TEST1*TEST1~CLP*TEST1~ST*TEST2*TEST2~CLP*TEST2~ST*TEST3*TEST3~CLP*TEST3~GE*GARBAGE*~CLP~TESTX
Output:
~ST*TEST1*TEST1~CLP*TEST1
~ST*TEST2*TEST2~CLP*TEST2
~ST*TEST3*TEST3~CLP*TEST3
Don't reinvent the wheel, use a regexp replace tool such as sed or JREPL.BAT:
call jrepl "^.*?~ST(.+?)~GE.*$" "'~ST'+$1.replace(/~ST/g,'\r\n$&')" /jmatch <in.txt >out.txt
The ~ cannot be used as the first character of a search string in the substring substitution syntax %VARIABLE:SEARCH_STRING=REPLACE_STRING%, because it is used to mark the substring expansion %VARIABLE:~POSITION,LENGTH% (type set/? for more information).
Supposing your text file contains a single line of text only and it does not exceed a size of about 8 kBytes, I see the following option for accomplishing your task. This script makes use of the substring substitution syntax %VARIABLE:*SEARCH_STRING=REPLACE_STRING%; the * defines to match everything up to the first occurrence of SEARCH_STRING:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
rem initialise constants:
set "INFILE=test_in.txt"
set "OUTFILE=test_out.txt"
set "SEARCH=ST"
set "TAIL=GE"
rem read single-line file content into variable:
< "%INFILE%" set /P "DATA="
rem remove everything before first `~%SEARCH%`:
set "DATA=~%SEARCH%!DATA:*~%SEARCH%=!"
rem call sub-routine, redirect its output:
> "%OUTFILE%" call :LOOP
endlocal
goto :EOF
:LOOP
rem extract portion right to first `~%SEARCH%`:
set "RIGHT=!DATA:*~%SEARCH%=!"
rem skip rest if no match found:
if "!RIGHT!"=="!DATA!" goto :TAIL
rem extract portion left to first `~%SEARCH%`, including `~`:
set "LEFT=!DATA:%SEARCH%%RIGHT%=!"
rem the last character must be a `~`;
rem so remove it; `echo` outputs a trailing line-break;
rem the `if` avoids an empty line at the beginning;
rem the unwanted part at the beginning is removed implicitly:
if not "!LEFT:~,-1!"=="" echo(!LEFT:~,-1!
rem output `~%SEARCH%` without trailing line-break:
< nul set /P "DUMMY=~%SEARCH%"
rem store remainder for next iteration:
set "DATA=!RIGHT!"
rem loop back if remainder is not empty:
if not "!DATA!"=="" goto :LOOP
:TAIL
rem this section removes the part starting at `~%TAIL%`:
set "RIGHT=!DATA:*~%TAIL%=!"
if "!RIGHT!"=="!DATA!" goto :EOF
set "LEFT=!DATA:%TAIL%%RIGHT%=!"
rem output part before `~%TAIL%` without trailing line-break:
< nul set /P "DUMMY=!LEFT:~,-1!"
goto :EOF
The following restrictions apply to this approach:
the input file contains a single line;
the size of the input file does not exceed about 8 kBytes;
there is exactly one instance of ~GE, that occurs after all instances of ~ST;
there is always at least one character in between two adjacent ~ST instances;
no special characters occur in the file, like: SPACE, TAB, ", %, !, =;
1) Quick example:
set a="Hello"
set d="a"
Now, how do I get the value of "a" using (the value of) variable d? For example, the variable name could have been entered by a user using a prompt, or the name of the variable could have been sent to a function.
None of these ideas work:
set e=%%d%%
set e=%%%d%%%
set e=set e=%%d%%
%e%
After an hour of brainstorming and Googling, I have come up with this, but it seems too complicated and clumsy - is there really no other/easier way?:
set a="Hello"
set b="Good day"
set c="Good night"
set /p d="Give me a variable name"
call :GetVarVal %%%d%%% "e"
REM This now gives the correct value:
echo %e%
goto :eof
:GetVarVal
set "%~2=%~1"
goto :eof
2) Also, sort of similar, is there a better way to do this (ideally without a custom function):
set a="C:\Users\Blah\Documents\MP4Box\MP4Box.exe"
call :get_drive_and_path a
echo %b%
goto :eof
:get_drive_and_path
set b=%~dp1
goto :eof
Thanks!
example without delayed expansion (preserves exclamation marks):
#echo off &setlocal
set "a=Hello!"
set "d=a"
call echo %%%d%%%
output:
Hello!
If I understood you correctly, the trick called "delayed expansion" should work:
SETLOCAL ENABLEDELAYEDEXPANSION
SET a=Hello
SET b=a
ECHO This is the value of a: !%b%!
SETLOCAL DISABLEDELAYEDEXPANSION
Check how it works here
Part 2
set file="C:\Users\Blah\Documents\MP4Box\MP4Box.exe"
for %%a in (%file%) do echo %%~dpa
its just easy:
as you type e.g.
SET VarName=VarValue
Then the SET-function gets given a string looking like "VarName=VarValue" - it does not matter whether you use the marks there or not, the following commands do the same:
SET VarName=VarValue
SET "VarName=VarValue"
so now reflecting, what tools we have to build a string, we come up to the idea to use this:
SET "Name=VarName"
SET "%VarName%=VarValue"
Echo %Varname%
Better late than never - have a good time
ok basicly i want a simple batch program to change X characters to Y characters in a way like this
a-b
b-c
c-d
d-e
e-f
etc etc etc
ive looks up strings and other variable tricks but it doesnt work. heres what i tried and you can see on "codeb" where i tried an alternate method
set /p code=[paste your code here]
set code=%codea:~1%
set /a %code:~2% %codeb%
this was basically my way of attempting to split all the input characters into seperate variables.
if your feeling....bored below is the exact conversion for the translation
a='
b=v
c=x
d=s
e=w
f=d
g=f
h=g
i=u
j=h
k=j
l=k
m=n
n=b
o=i
p=o
q=\
r=e
s=a
t=r
u=y
v=c
w=q
x=z
y=t
z=/
essentially i should be able to "paste" this "v'rxg" into the batch and hit enter and it then displays "batch"
I modified Andriy's code for a faster version that use Batch variables instead of FIND text file. Here it is:
#echo off
setlocal EnableDelayedExpansion
for /f "delims=" %%a in (codechart.txt) do set %%a
:loop
set /P encoded=[paste your code here]
if "%encoded%" == "" goto :eof
set decoded=
call :decode
echo Result:
echo\%decoded%
goto loop
:decode
if "%encoded%" == "" goto :eof
set ec=%encoded:~0,1%
set encoded=%encoded:~1%
set dc=?
if defined %ec% set dc=!%ec%!
set decoded=%decoded%%dc%
goto decode
However, my version does not work when special characters (non-valid variable names) are encoded.
EDIT The only differences of my code vs. Andriy's are these:
setlocal EnableDelayedExpansion: This command is required to use !var! expansion besides %var%, allowing to use both expansions in the same command.
for /f "delims=" %%a in (codechart.txt) do set %%a: This command takes the lines of your conversions and execute a set command with each one, that is, set a=' set b=v etc. This way, the conversions are stored in Batch variables with the name of the original character and the value of the new character.
if defined %ec% set dc=!%ec%!: This line convert the character in ec variable via a direct replacement. For instance, if ec is "b": if defined b set dc=!b!; because b variable exists and contains a "v", then execute set dc=v.
Well, here's a working script that you can play with. It uses your chart in the form of a text file (named codechart.txt in my setup):
#ECHO OFF
:loop
SET /P encoded=[paste your code here]
IF [%encoded%]==[] GOTO :EOF
SET decoded=
CALL :decode
ECHO Result:
ECHO(%decoded%
GOTO loop
:decode
IF [%encoded%]==[] GOTO :EOF
SET ec=%encoded:~0,1%
SET encoded=%encoded:~1%
SET dc=?
FOR /F "delims==" %%C IN ('FIND "=%ec%" ^<codechart.txt') DO SET dc=%%C
SET decoded=%decoded%%dc%
GOTO decode
If you enter a non-empty string, it will produce a result. Hitting Enter without actually entering anything will terminate the batch. If a character is not found in the chart, it will be represented as a ? in the output.