3

Lets say we have a variable like;

set test=This is a Test string.

and we want to replace every lower-case t with upper-case X so desired output;

This is a TesX sXring.

i tryed using string manipulation set test=%test:t=X% but its replacing every t with X. I also tryed JREPL.bat from @dbenham but cant work it out.

John DOE
  • 400
  • 1
  • 3
  • 18

4 Answers4

6

There is no native batch command that can conveniently do case sensitive string manipulation. A native batch solution would have to build a new string character by character in a loop. Very doable - but a pain and also inefficient.

EDIT - Long ago I wrote a pure batch utility called modFile.bat that does a case sensitive find/replace on the content of a text file. Performance isn't bad, but it does have some restrictions. I rarely use that code. I had written a companion routine to do case sensitive find/replace on a variable instead of a text file, but I seem to have lost that code.

Today I pretty much always use JREPL.BAT whenever I want to manipulate text.

The JREPL.BAT solution for your example should be:

for /f delims^=^ eol^= %%A in ('jrepl t X /s test') do set "test=%%A"

But there seems to be a bug. It works if the variable name is some name other than test. Looks like I have some debugging to do.

Until the bug is fixed, you can use:

for /f delims^=^ eol^= %%A in ('cmd /v:on /c "echo(!test!)|jrepl t X"') do set "test=%%A"


Update: The bug has been fixed in version 3.4 The /S option now works with a variable named TEST.

dbenham
  • 127,446
  • 28
  • 251
  • 390
  • you are using a `test` variable in your `get options` batch code that masks the input variable – MC ND Jan 22 '15 at 18:07
  • May i ask what `for /f delims^=^ eol^= ` is it for ? and why there is a caret ? Can you explain a bit ? – John DOE Jan 22 '15 at 18:07
  • @MCND - Yes, I saw the problem as soon as I looked at the code. I've switched to a variable named `/TEST`, and updated the documentation to point out limits in variable names. Generally, anything that doesn't begin with `/`. Specifically, cannot match an option name nor `/TEST`. – dbenham Jan 22 '15 at 19:32
  • 1
    @JohnDOE - The FOR /F command processes the output of the command. The arcane DELIMS and EOL syntax is the only way to disable both options to guarantee all values are preserved, no matter what the leading character is. It is not needed with your specific example, but generally you might not know what the leading character is, and you don't want it to match the EOL character. – dbenham Jan 22 '15 at 19:35
  • There is a simple and efficient pure Batch method to do a case sensitive replacement of _one letter_ by another one. See [my solution](http://stackoverflow.com/questions/28095092/string-manipulation-with-case-sensitivity/28099702#28099702)... – Aacini Jan 24 '15 at 22:43
  • @dbenham I tryed JREPL and its working wonderful but there is a small problem. is it possible to replace `Ö ö Ç ç Ş ş İ ı Ğ ğ Ü ü` with `O o C c S s I i G g U u`. When i try its just messing up everything. I tryed different code pages but still there is a problem with those letters. Am i missing something ? – John DOE Jan 28 '15 at 15:53
  • It can be tricky, but it is possible to get JREPL to properly manipulate unicode values. But your subsequent [REN command](http://stackoverflow.com/q/28177634/1012053) fails due to limitations of the the command line. I've [answered your REN question](http://stackoverflow.com/a/28204254/1012053) by using JREN.BAT instead. – dbenham Jan 28 '15 at 23:24
2

To keep it in pure batch, you can do a char by char case-sensitive comparison:

@ECHO OFF
SETLOCAL EnableDelayedExpansion

SET String=This is a test.
SET "Result="

REM Process each char.
FOR /L %%A IN (0,1,999) DO (
    SET Char=!String:~%%A,1!
    REM Case sensitive char replacement.
    IF "!Char!" EQU "t" SET Char=X

    REM Append result.
    SET Result=!Result!!Char!
)

REM Output will be: This is a XesX.
ECHO %Result%

ENDLOCAL
Jason Faulkner
  • 6,378
  • 2
  • 28
  • 33
  • Sure, this is relatively simple to search and replace a single character. But it becomes more complicated if both the search and replacement string lengths can vary. I had written such a routine before, but it is pain. You really need to know the length of your input string and your search string. – dbenham Jan 25 '15 at 00:13
  • @dbenham - Agreed that this isn't the most efficient method as you always have to do a position by position comparison and batch doesn't have a native way to get string lengths. Best "optimization" of this method I have been able to find is to short-circuit the loop by looking for an empty string and then exiting so do you don't always run through 999 iterations. – Jason Faulkner Jan 25 '15 at 12:44
  • See http://stackoverflow.com/a/5841587/1012053 for a very fast batch strlen function. Also, have a look at the modfile.bat link in my [edited answer](http://stackoverflow.com/a/28095611/1012053). – dbenham Jan 25 '15 at 13:36
2

If you don't need full jrepl.bat functionality, you could just make your own hybrid batch script to use JScript's String.prototype.replace() functionality.

@if (@CodeSection == @Batch) @Then
@echo off
setlocal

set test=This is a Test string.

:: invoke JScript and capture result to %test%
for /f "delims=" %%I in ('cscript /nologo /e:Jscript "%~f0" "%test%"') do (
    set "test=%%I"
)

echo %test%

goto :EOF

:: end batch / begin JScript
@end

WSH.Echo(WSH.Arguments(0).replace(/t/g, 'X'));

Output:

This is a TesX sXring.

Or if you don't mind a PowerShell command, -creplace does case-sensitive replacement.

@echo off
setlocal

set test=This is a Test string.
set psCommand=powershell -command "'%test%' -creplace 't','X'"

for /f "delims=" %%I in ('%psCommand%') do set "test=%%I"

echo %test%

Same output.

rojo
  • 24,000
  • 5
  • 55
  • 101
0

I don't think that the pure Batch solution below be inefficient (the replacement is achieved in just two lines with no loops). Its only restriction is that it replaces a maximum of 25 letters in the string:

EDIT: I fixed a bug that incorrectly replaced several search letters that are joined together by just one replacement letter.

@echo off
setlocal EnableDelayedExpansion

set find=t
set repl=X

rem Prepare the replacement auxiliary variables (just once)
set "join=%%a"
for %%a in (b c d e f g h i j k l m n o p q r s t u v w x y z) do set "join=!join!%repl%%%%%a"

rem First method: fail when two search letters are joined together
set test=This is a Test string.

echo Input:  %test%
echo Change "%find%" by "%repl%":
for /F "tokens=1-26 delims=%find%" %%a in ("%test%¡") do call set "out=%join%"
for /F "delims=¡" %%a in ("%out%") do set "out=%%a"
echo Output: %out%

echo/

rem Second method: previous bug fixed
set test=This Test String Have Two "t" Letters joined Together.

echo Input:  %test%
echo Change "%find%" by "%repl%":
for /F "tokens=1-26 delims=%find%" %%a in ("!test:%find%%find%=%find%¿%find%!¡") do call set "out=%join%"
for /F "delims=¡" %%a in ("%out%") do set "out=%%a"
set "out=!out:%repl%¿%repl%=%repl%%repl%!"
echo Output: %out%

Output:

Input:  This is a Test string.
Change "t" by "X":
Output: This is a TesX sXring.

Input:  This Test String Have Two "t" Letters joined Together.
Change "t" by "X":
Output: This TesX SXring Have Two "X" LeXXers joined TogeXher.
Aacini
  • 65,180
  • 12
  • 72
  • 108
  • Major problems still, even with the 2nd method with "bug fix": Try with `set "test=t T tt TT ttt TTT tttt TTTT"`. Result=`T XX XX XX XXT XX┐X XX┐X` – dbenham Jan 24 '15 at 23:07