Improved :Format, new :FormatVar and :FormatColor functions

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

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

Improved :Format, new :FormatVar and :FormatColor functions

#1 Post by dbenham » 02 Dec 2012 19:50

The DosTips :Format function is pretty cool and clever.

For those that have never seen it, the function takes a format string followed by up to 8 string arguments. The format string can contain literal text and variable place holders enclosed in square brackets. The place holders are positional, the 1st gets the 1st argument after the format, the 2nd the 2nd argument, etc. The number inside the brackets specifies the width of the output. Positive numbers are left justified, negative numbers right justified.

But the function is is relatively slow. It makes heavy use of CALL, and it is limited to a maximum of 8 string substitutions within the format string.

In response to a SS64 forum question from AiroNG, I have developed an optimized version of :Format that is ~3 times faster. I've eliminated all but one internal CALL by using delayed expansion, and I use SHIFT to eliminate the limit on the number of substitutions.

Code: Select all

@echo off
setlocal
set "format=Name: [25] Origin: [15] Age: [-3]"
call :Format "%format%" "Gerd Mueller" "Berlin" 45
call :Format "%format%" "Sally Whiteoak" "New York" 9
call :Format "%format%" "Richard Edgarson" "New Orleans" 103
exit /b

:Format Fmt [Str1] [Str2]...
setlocal disableDelayedExpansion
set "fmt=%~1"
set "line="
set "space=                                                                                                    "
setlocal enableDelayedExpansion
for %%n in (^"^

^") do for /f "tokens=1,2 delims=[" %%a in (".!fmt:]=%%~n.!") do (
  if "!!" equ "" endlocal
  set "const=%%a"
  call set "subst=%%~2%space%%%~2"
  setlocal enableDelayedExpansion
  if %%b0 geq 0 (set "subst=!subst:~0,%%b!") else set "subst=!subst:~%%b!"
  for /f delims^=^ eol^= %%c in ("!line!!const:~1!!subst!") do (
    endlocal
    set "line=%%c"
  )
  shift /2
)
setlocal enableDelayedExpansion
echo(!line!
exit /b

Output:

Code: Select all

Name: Gerd Mueller              Origin: Berlin          Age:  45
Name: Sally Whiteoak            Origin: New York        Age:   9
Name: Richard Edgarson          Origin: New Orleans     Age: 103

I've also written a :FormatVar version that is yet 30% faster. This version passes all strings by reference. Only the name of the format variable is passed as an argument. Within each set of square brackets is the name of the variable containing the substitution string, optionally followed by a comma and the width. Again, positive is left justified, negative is right justified. If the width is missing, then the contents of the variable are inserted without any padding. This is useful to insert square bracket literals into the output.

The following code produces the exact same output as my first code. It is a good candidate to be converted to a macro ;)

Code: Select all

@echo off
setlocal
set "format=Name: [name,25] Origin: [origin,15] Age: [age,-3]"

set "name=Gerd Mueller"
set "origin=Berlin"
set "age=45"
call :FormatVar format

set "name=Sally Whiteoak"
set "orign=New York"
set "age=103"
call :FormatVar format

set "name=Richard Edgarson"
set "origin=New Orleans"
set "age=9"
call :FormatVar format
exit /b

:FormatVar  FmtVar
setlocal disableDelayedExpansion
set "line="
set "space=                                                                                                    "
setlocal enableDelayedExpansion
for %%n in (^"^

^") do for /f "tokens=1,2 delims=[" %%a in (".!%~1:]=%%~n.!") do (
  if "!!" equ "" endlocal
  set "const=%%a"
  set "subst="
  setlocal enableDelayedExpansion
  for /f "tokens=1,2 delims=," %%c in ("%%b") do (
    if "%%d" equ "" (set "subst=!%%c!") else (
      set "subst=!%%c!%space%!%%c!"
      if %%d0 geq 0 (set "subst=!subst:~0,%%d!") else set "subst=!subst:~%%d!"
    )
  )
  for /f delims^=^ eol^= %%c in ("!line!!const:~1!!subst!") do (
    endlocal
    set "line=%%c"
  )
)
setlocal enableDelayedExpansion
echo(!line!
exit /b


I printed the same 3 lines 40 times (total of 120 lines) with all 3 versions and came up with the following timings:

DosTips :Format - 4.5 seconds
My :Format - 1.5 seconds
My :FormatVar - 1.0 seconds

Finally, I integrated the :color routines that have been worked on in the recent Carlos post. The color technique is inherently slow, and a real dog on XP. But it works :)

My :FormatColor function has similar syntax to my :FormatVar function. Except now color change specifiers may be embedded within curly braces anywhere within the format string. The color is specified using the same hex notation as is used by the COLOR command. The initial color defaults to 07 - white text on black background. I've changed the output a bit to show how variables can be used to incorporate curly brace and square bracket literals into the output.

Code: Select all

@echo off
setlocal
set "lc={"
set "rc=}"
set "ls=["
set "rs=]"
set "format=Name: {0E}[lc][name,25][rc]{07} Origin: {0C}[ls][origin,15][rs]{07} Age: {0A}[age,-3]"

set "name=Gerd Mueller"
set "origin=Berlin"
set "age=45"
call :formatColor format

set "name=Sally Whiteoak"
set "orign=New York"
set "age=9"
call :formatColor format

set "name=Richard Edgarson"
set "origin=New Orleans"
set "age=103"
call :formatColor format
exit /b

:formatColor fmt
if not defined DEL call :initColorPrint
setlocal disableDelayedExpansion
set "color=07"
set "line="
set "space=                                                                                                    "
setlocal enableDelayedExpansion
for %%n in (^"^

^") do for /f "tokens=1,2 delims={" %%x in (".!%~1:}=%%~n.!") do (
  if "!!" equ "" endlocal
  set "str=%%x"
  setlocal enableDelayedExpansion
  for /f "tokens=1,2 delims=[" %%a in ("!str:]=%%~n.!") do (
    endlocal
    set "const=%%a"
    set "subst="
    setlocal enableDelayedExpansion
    for /f "tokens=1,2 delims=," %%c in ("%%b") do (
      if "%%d" equ "" (set "subst=!%%c!") else (
        set "subst=!%%c!%space%!%%c!"
        if %%d0 geq 0 (set "subst=!subst:~0,%%d!") else set "subst=!subst:~%%d!"
      )
    )
    set "const=!const:~1!"
    if defined const call :colorPrintVar !color! const
    if defined subst call :colorPrintVar !color! subst
  )
  endlocal
  if "%%y" neq "" set "color=%%y"
)
(echo()
exit /b


:colorPrintVar  Color  StrVar  /n
if not defined DEL call :initColorPrint
setlocal enableDelayedExpansion
pushd .
':
cd \
set "s=!%~2!"
:: The single blank line within the following IN() clause is critical - DO NOT REMOVE
for %%n in (^"^

^") do (
  set "s=!s:\=%%~n\%%~n!"
  set "s=!s:/=%%~n/%%~n!"
  set "s=!s::=%%~n:%%~n!"
)
set "s=!s:"=\"!"
for /f delims^=^ eol^= %%s in ("!s!") do (
  if "!" equ "" setlocal disableDelayedExpansion
  if %%s==\ (
    findstr /a:%~1 "." "\'" nul
    <nul set /p "=%DEL%%DEL%%DEL%"
  ) else if %%s==/ (
    findstr /a:%~1 "." "/.\'" nul
    <nul set /p "=%DEL%%DEL%%DEL%%DEL%%DEL%"
  ) else (
    findstr /a:%~1 "." "%%s\..\'" nul
    <nul set /p "=%DEL%%DEL%%DEL%%DEL%%DEL%%DEL%%DEL%"
  )
)
if /i "%~3"=="/n" echo(
popd
exit /b


:initColorPrint
for /f %%A in ('"prompt $H&for %%B in (1) do rem"') do set "DEL=%%A %%A"
<nul >"%temp%\'" set /p "=."
subst ': "%temp%" >nul
exit /b


:cleanupColorPrint
2>nul del "%temp%\'"
>nul subst ': /d
exit /b

I can't show fixed width output with color. But the name is in yellow, the origin in blue, and the age in green :D

No edit was made - I momentarily lost my mind and broke the code, and have since reverted to working code :roll:

Dave Benham
Last edited by dbenham on 02 Jan 2013 17:16, edited 2 times in total.

carlos
Expert
Posts: 503
Joined: 20 Aug 2010 13:57
Location: Chile
Contact:

Re: Improved :Format, new :FormatVar and :FormatColor functi

#2 Post by carlos » 03 Dec 2012 17:20

Dave in the colorPrintVar I found that this line:

Code: Select all

  if "!" equ "" setlocal disableDelayedExpansion


is very unnecessary because it will be executed for every special character.
This mean that you run this routine:

Active DelayedExpansion
Split the line line in LF
Reading every slice:
{
If DelayedExpansion is active: disable
...
}

this mean that this line is executed 1...the numbers of slices of the line.
And when the condition is true (only the first time), then delayed expansion is always disabled, then the if condition is unnecessary, because it will be even false.

I fix this in my color function version 17, but the idea could be implement in your code, replacing this:

Code: Select all

for /f delims^=^ eol^= %%s in ("!s!") do (
  if "!" equ "" setlocal disableDelayedExpansion
  if %%s==\ (


for this:

Code: Select all

for /f delims^=^ eol^= %%# in ("!s!") do (
setlocal disabledelayedexpansion
for /f delims^=^ eol^= %%s in (
"%%~#") do if %%s==\ (

and adding the corresponding endlocal to the end.

With this line the disabled of delayed expansion is equally executed one time, but the evaluation of a if that is even false after the first execution is removed, and the speed is increasy again.


Also, I found that this line is used:

Code: Select all

cd \

If you care of someone use the volume and change the root folder, this mean that someone is using this volume. Then you also will be restore the previous folder in the volume maybe adding other pushd and other popd.
I found this unnecessary, because the code relies in that the volume drive letter is very few knowed and used.

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

Re: Improved :Format, new :FormatVar and :FormatColor functi

#3 Post by dbenham » 03 Dec 2012 18:13

Hi Carlos. I like the fact that you concern yourself with batch performance - most people don't. But... you need to be more careful with your assertions.

My logic is

Code: Select all

for ... do (
  if delayedExpansionEnabled setlocal disableDelayedExpansion
  ...
)

And you think the following is faster :shock:

Code: Select all

for ... do (
  setlocal disableDelayedExpansion
  ...
  endlocal
)

I pretty much knew the answer to the question, but I ran the test to prove it.

Code: Select all

@echo off
setlocal enableDelayedExpansion

echo My way with IF
echo %time%
for /l %%N in (1 1 10000) do (
  if "!"=="" setlocal disableDelayedExpansion
)
echo %time%

echo(
echo Carlos way without IF
echo %time%
for /l %%N in (1 1 10000) do (
  setlocal disableDelayedExpansion
  endlocal
)
echo %time%

Results:

Code: Select all

My way with IF
18:52:30.10
18:52:30.14

Carlos way without IF
18:52:30.15
18:52:32.72

My way with the single IF statement that evaluates to TRUE only once executes ~64 times faster than your way that toggles the delayed expansion off and on every iteration. And that was run with a "normal" size environment. The difference can increase dramatically as the size of the environment increases.

I have gained a lot of experience optimizing batch code for performance, especially with my work on the macro project (along with others). I've gotten pretty good at anticipating what is optimal. But sometimes I am surprised by actual results.

Nothing beats actual testing :wink:

Given your attempts to optimize code, you really should do more performance testing to get a better handle on what are the real performance bottlenecks. Besides better optimizations, I think you will find that you can reformat your code to be more readable without sacrificing much in the way of performance.

Regarding the CD \, I know it is normally not needed. But if it is not there, then the code would break if anyone changed the directory for the virtual drive. The CD command is only executed once per call, so I thought it was worth including for peace of mind.

carlos
Expert
Posts: 503
Joined: 20 Aug 2010 13:57
Location: Chile
Contact:

Re: Improved :Format, new :FormatVar and :FormatColor functi

#4 Post by carlos » 03 Dec 2012 18:28

Maybe I not explained good. I explain that you run the disable of delayed expansion before the main FOR not inside it.
This is the tested fixed code that reflect my comment:

Code: Select all

@echo off
setlocal enableDelayedExpansion

echo My way with IF
echo %time%
for /l %%N in (1 1 10000) do (
  if "!"=="" setlocal disableDelayedExpansion
)
echo %time%

echo(

echo Carlos way without IF
echo %time%
for %%# in (1) do (
 setlocal disableDelayedExpansion
  for /l %%N in (1 1 10000) do (
    rem
  )
  endlocal
)
echo %time%


These are my results.

Code: Select all

My way with IF
21:24:32,72
21:24:32,84

Carlos way without IF
21:24:32,85
21:24:32,89


Your (Dave's) takes 12 milliseconds and Carlos takes 4 milliseconds.

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

Re: Improved :Format, new :FormatVar and :FormatColor functi

#5 Post by dbenham » 03 Dec 2012 20:58

Not really important, but those are centiseconds (1/100) not milliseconds (1/1000).

The test you ran is not a fair representation of your changes. My loop must be entered with delayed expansion on, and within the loop it must be off. The loop can have multiple iterations.

With my IF method, the IF statement is executed once per iteration, and the SETLOCAL is only executed once within the loop, and the ENDLOCAL once after the loop finishes.

With your method, the SETLOCAL and ENDLOCAL must be executed once per iteration. Your extra inner FOR loop serves no purpose. You are doing nothing but transferring a value from one FOR variable to another. The iterations are being generated by the outer loop. The inner loop only iterates once per outer loop iteration. But the total number of outer loop iterations doesn't change.

My original test is not very representative because a call to the function will not have 10000 iterations. Most calls will likely have fewer than 10 iterations.

Here is a better test that shows the difference using 1, 2, 5, and 10 iterations. My test uses FOR /L to control the number of iterations, but the actual code uses FOR /F with a string that has multiple lines, one iteration per line.

I run each test 10000 times just to get the total time long enough to be measured effectively.

Code: Select all

@echo off
setlocal enableDelayedExpansion

for %%N in (1 2 5 10) do (

  echo -----------------------------
  echo %%N iterations

  echo(
  echo Dave way with IF
  echo !time!
  for /l %%A in (1 1 10000) do (
    for /l %%B in (1 1 %%N) do (
      if "!"=="" setlocal disableDelayedExpansion
    )
    endlocal
  )
  echo !time!

  echo(

  echo Carlos way without IF
  echo !time!
  for /l %%A in (1 1 10000) do (
    for /l %%B in (1 1 %%N) do (
      setlocal disableDelayedExpansion
      endlocal
    )
  )
  echo !time!
)

Here are the results, with some elapsed times added manually for clarity

Code: Select all

-----------------------------
1 iterations

My way with IF
20:26:54.19
20:26:57.33      -->  3.14

Carlos way without IF
20:26:57.33
20:27:00.44      -->  3.11
-----------------------------
2 iterations

My way with IF
20:27:00.44
20:27:03.69      -->  3.25

Carlos way without IF
20:27:03.69
20:27:09.68      -->  5.99
-----------------------------
5 iterations

My way with IF
20:27:09.69
20:27:13.12      -->  3.43

Carlos way without IF
20:27:13.12
20:27:27.99      --> 14.87
-----------------------------
10 iterations

My way with IF
20:27:27.99
20:27:31.73      -->  3.74

Carlos way without IF
20:27:31.73
20:28:01.26      --> 29.53


Your method is barely faster only when there is a single iteration. It makes sense that yours is faster with 1 iteration because my way is executing an IF and yours is not. The number of SETLOCAL and ENDLOCAL is the same for both when there is only 1 iteration. The tiny difference in times is a measure of how long it takes for the IF statement to execute: only 0.03 seconds for 10000 IF statements - that is pretty darn fast.

Increase the number of iterations to 2 and now my method is significantly faster, almost a factor of 2. That is because the time to perform an IF is nearly inconsequential compared to the time it takes to perform SETLOCAL and ENDLOCAL. I'm doing 2 IF and 1 SETLOCAL/ENDLOCAL. You are doing 2 SETLOCAL/ENDLOCAL.

The difference in performance becomes more and more noticeable as the number of iterations increases.

In reality, it doesn't much matter which method is used because there are other parts of the :colorPrintVar algorithm that are much slower than either of our methods. The difference in performance between our methods is probably inconsequential compared to the total amount of time it takes for the complete :printColorVar function to execute.

But given that my method is faster than yours except for the 1 iteration case, I will stick with my IF method. Especially since the amount of extra code is trivial.


Dave Benham

carlos
Expert
Posts: 503
Joined: 20 Aug 2010 13:57
Location: Chile
Contact:

Re: Improved :Format, new :FormatVar and :FormatColor functi

#6 Post by carlos » 04 Dec 2012 09:28

The best benchmark is not changes the scenario. Please testing you original code, and the same but with my suggested modification (for avoid confussions I put it in the download link) (You can compare with WinMerge). I do the test using timethis.exe and always my suggestion writing in code consume less time. Edit: I changes the order of the lines in test.cmd and the first line that use timethis report less time. Maybe for this, the conclusion are not concluyent.

Here I put the batch files for the test:
https://dl.dropbox.com/s/fmskstptuv98c9i/tests.zip?dl=1

Edit: after read your comments, I realize that I run the setlocal endlocal many times where the text variable have LF inserted if I use it inside a for / f. My original idea was put inside a single for it, but eventually used for / f for a problem with some type of variables. Because this the for /f was unintentional.

Code: Select all

@echo off
SetLocal EnableDelayedExpansion

set "var=quote:" "

Echo Single For:
For %%# in ("!var!") do Echo(%%~#

Echo For /F:
For /F delims^=^ eol^= %%# in ("!var!") do Echo(%%~#



Prints:

Code: Select all

Single For:
quote:

For /F:
quote:"

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

Re: Improved :Format, new :FormatVar and :FormatColor functi

#7 Post by Aacini » 04 Dec 2012 15:24

I would like to present an alternative solution to this topic.

My Show.exe auxiliary program may display variable values in fields of certain width:

Code: Select all

Show code[*times] | "literal" | variable[:[±]wide] ...
If the wide have no sign the value is left justified, if have minus sign it is right justified, and if have plus sign the value is centered in the field. If the value exceed field width, it is cut at the appropriate side(s). The parameter for Show command may be assembled in a way that contains variable references (like a format string), as we used in other similar cases. For example:

Code: Select all

@echo off
setlocal
set format="Name: " name:25 " Origin: " origin:15 " Age: " age:-3  13 10

set "name=Gerd Mueller"
set "origin=Berlin"
set "age=45"
Show %format%

set "name=Sally Whiteoak"
set "orign=New York"
set "age=9"
Show %format%

set "name=Richard Edgarson"
set "origin=New Orleans"
set "age=103"
Show %format%
An interesting point of Show.exe is that a variable name may include a FOR replaceable parameter as subscript, so array elements may be processed with no need of Delayed Expansion:

Code: Select all

@echo off
setlocal DisableDelayedExpansion
set format="Name: " name[%%i]:25 " Origin: " origin[%%i]:15 " Age: " age[%%i]:-3  13 10

set "name[1]=Gerd Mueller"
set "origin[1]=Berlin"
set "age[1]=45"

set "name[2]=Sally Whiteoak"
set "origin[2]=New York"
set "age[2]=9"

set "name[3]=Richard Edgarson"
set "origin[3]=New Orleans"
set "age[3]=103"

for /L %%i in (1,1,3) do (
   Show %format%
)

Output:

Code: Select all

Name: Gerd Mueller              Origin: Berlin          Age:  45
Name: Sally Whiteoak            Origin: New York        Age:   9
Name: Richard Edgarson          Origin: New Orleans     Age: 103
ColorShow.exe is the color version of Show.exe. A color attribute may be inserted at any place by a slash and a color code, and it set the color of following parameters until a new color is given. The initial color is the color at current cursor position. To change back to that initial color, use a slash alone; a double slash indicate the initial color in reverse video. This feature is useful to easily show the selected option of a menu.

Code: Select all

@echo off
setlocal
set "lc={"
set "rc=}"
set "ls=["
set "rs=]"
set format="Name: " /0E lc name:25 rc / " Origin: " /0C ls origin:15 rs / " Age: " /0A age:-3  13 10

set "name=Gerd Mueller"
set "origin=Berlin"
set "age=45"
ColorShow %format%

set "name=Sally Whiteoak"
set "orign=New York"
set "age=9"
ColorShow %format%

set "name=Richard Edgarson"
set "origin=New Orleans"
set "age=103"
ColorShow %format%
Output of previous program should be the same of Dave's color example above. However, if default screen color is changed with COLOR command, the output will differ because Dave's version will change color of "Name:", "Origin:" and "Age:" strings to white text on black background.

I printed the same 3 lines 40 times (total of 120 lines) of Dave's test with the 2 color versions and came up with the following timings:

Dave's Findstr.exe method: 1 minute 14 seconds
My ColorShow.exe method: less than 1 second

Besides the complexity of the procedure required to achieve Findstr to show a string in color, other point that have a bearing on these timings is the fact that findstr.exe is a 27 KB size file that must be executed for each color change in a line. On the other hand, colorshow.exe is only 3.5 KB and can show a line with multiple color changes. If a line would need even more color changes, like to print Ascii-Art in color (www .dostips.com/forum/viewtopic.php?f=3&t=4018&p=22321#p22321), the timing differences between the two versions may be huge!

Antonio

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

Re: Improved :Format, new :FormatVar and :FormatColor functi

#8 Post by dbenham » 05 Dec 2012 00:33

Hi Aacini

Your utility exe programs are a nice addition, and as would be expected, there is no comparison with performance - exe wins hands down.

But there are some corporate policies that forbid downloading/installing foreign exe files, so it is good to also have pure native batch solutions.

@Carlos - My results are completely opposite from what you report. I use the following utility script to compute times.

diffTime.bat

Code: Select all

:diffTime  startTimeVar  stopTimeVar  rtnVar
setlocal enableDelayedExpansion
for /f "tokens=1-4 delims=:." %%a in ("!%~1!") do set /a t1=(((1%%a*60)+1%%b)*60+1%%c)*100+1%%d-36610100
for /f "tokens=1-4 delims=:." %%a in ("!%~2!") do set /a t2=(((1%%a*60)+1%%b)*60+1%%c)*100+1%%d-36610100
set /a "day=24*60*60*100, diff=(t2-t1+day)%%day, sec=diff/100, frac=diff%%100"
if %frac% lss 10 set "frac=0%frac%"
set "diff=%sec%.%frac%"
endlocal & if "%~3" neq "" (set "%~3=%diff%") else echo %diff%

I test each of our routines with 3 different strings. The 1st one is printed in 1 step, the 2nd requires 3 steps, and the last one 5 steps. Each string is tested 100 times to get a measurable time. I redirect the actual output to nul so that screen update and scroll times are taken out of the equation.

dave.bat

Code: Select all

@echo off
setlocal enableDelayedExpansion
set "text1=This version works in Win XP, 7 & 8 "Hurray^^^!""
set "text2=This version works in Win XP, 7 & 8: "Hurray^^^!""
set "text3=\This version works in Win XP, 7 & 8: "Hurray^^^!"/"
for %%V in (text1 text2 text3) do (
  echo !%%V!
  set t1=!time!
  for /l %%N in (1 1 %1) do call :colorPrintVar 9b %%V /n >nul
  set t2=!time!
  call diffTime.bat t1 t2
  echo(
)
exit /b


:colorPrint  Color  Str  [/n]
if not defined DEL call :initColorPrint
setlocal disableDelayedExpansion
set "s=%~2"
call :colorPrintVar %1 s %3
exit /b


:colorPrintVar  Color  StrVar  [/n]
if not defined DEL call :initColorPrint
setlocal enableDelayedExpansion
pushd .
':
cd \
set "s=!%~2!"
:: The single blank line within the following IN() clause is critical - DO NOT REMOVE
for %%n in (^"^

^") do (
  set "s=!s:\=%%~n\%%~n!"
  set "s=!s:/=%%~n/%%~n!"
  set "s=!s::=%%~n:%%~n!"
)
set "s=!s:"=\"!"
for /f delims^=^ eol^= %%s in ("!s!") do (
  if "!" equ "" setlocal disableDelayedExpansion
  if %%s==\ (
    findstr /a:%~1 "." "\'" nul
    <nul set /p "=%DEL%%DEL%%DEL%"
  ) else if %%s==/ (
    findstr /a:%~1 "." "/.\'" nul
    <nul set /p "=%DEL%%DEL%%DEL%%DEL%%DEL%"
  ) else (
    findstr /a:%~1 "." "%%s\..\'" nul
    <nul set /p "=%DEL%%DEL%%DEL%%DEL%%DEL%%DEL%%DEL%"
  )
)
if /i "%~3"=="/n" echo(
popd
exit /b


:initColorPrint
for /f %%A in ('"prompt $H&for %%B in (1) do rem"') do set "DEL=%%A %%A"
<nul >"%temp%\'" set /p "=."
subst ': "%temp%" >nul
exit /b


:cleanupColorPrint
2>nul del "%temp%\'"
>nul subst ': /d
exit /b

carlos.bat

Code: Select all

@ECHO OFF
setlocal enableDelayedExpansion
set "text1=This version works in Win XP, 7 & 8 "Hurray^^^!""
set "text2=This version works in Win XP, 7 & 8: "Hurray^^^!""
set "text3=\This version works in Win XP, 7 & 8: "Hurray^^^!"/"
for %%V in (text1 text2 text3) do (
  echo !%%V!
  set t1=!time!
  for /l %%N in (1 1 %1) do call :color 9b %%V \n >nul
  set t2=!time!
  call diffTime.bat t1 t2
  echo(
)
exit /b


:Color
:: v17. Arguments: hexColor variableName [\n]
SetLocal EnableExtensions EnableDelayedExpansion
Subst `: "!Temp!" >Nul &Pushd . &`: &If Not Exist `.bat (
Set /P "=."<Nul >` &For /F "delims=;" %%# in (
'"Prompt;$H;&For %%_ in (1) Do Rem"') Do Echo(Set "b=%%#">`.bat
For %%# in ("(Set n=^" "" ")") Do Echo(%%~#>>`.bat)
Call ` &Set "c=%~1" &Set "t=!%~2!"
If Defined t For %%# in ("!n!") Do For %%_ in (\ / :
) Do Set "t=!t:%%_=%%~#%%_%%~#!"
For /F usebackq^ delims^=^ eol^= %%_ in ('!t:"=\"!') Do (
SetLocal DisableDelayedExpansion
For /F delims^=^ eol^= %%# in ("%%~_") Do If \==%%# (
Findstr /A:%c% "." "\`" Nul &Set /P "=%b%%b%%b%"<Nul
) Else If /==%%# (Findstr /A:%c% "." "/.\`" Nul
Set /P "=%b%%b%%b%%b%%b%"<Nul
) Else (Findstr /A:%c% "." "%%#\..\`" Nul
Set /P "=%b%%b%%b%%b%%b%%b%%b%"<Nul)
EndLocal)
If /I "\n"=="%~3" (Echo()
Popd &Goto :Eof

Results

Code: Select all

C:\test>dave 100
This version works in Win XP, 7 & 8 "Hurray!"
2.24

This version works in Win XP, 7 & 8: "Hurray!"
4.92

\This version works in Win XP, 7 & 8: "Hurray!"/
7.69


C:\test>carlos 100
This version works in Win XP, 7 & 8 "Hurray!"
3.61

This version works in Win XP, 7 & 8: "Hurray!"
6.41

\This version works in Win XP, 7 & 8: "Hurray!"/
9.18


I'm not claiming my code can't be further optimized. But the changes you are suggesting in the interest of performance are not helping. And I find your code extremely difficult to read, mostly because of excessive compound statements and lack of indents. I'm assuming you have adopted your coding style for performance, but I don't see the benefit.


Dave Benham

carlos
Expert
Posts: 503
Joined: 20 Aug 2010 13:57
Location: Chile
Contact:

Re: Improved :Format, new :FormatVar and :FormatColor functi

#9 Post by carlos » 13 Dec 2012 19:19

Dave. You are right. For me is a contradiction, but anyways your code run some time most speedy that any of my versions, that are also fasters.

Post Reply