Append Text with batch file - batch-file

I use the following code to write a 5 times to hi.txt using batch file.
The problem is tht it automatically appends newline to the end.
Output:
a
a
a
a
a
I want:
aaaaa

The method below is probably the fastest one to create a file with a given number of the same character. If the number is just 10,000 the file is created in an instant.
#echo off
setlocal EnableDelayedExpansion
set times=10000
rem Create the initial file with one "a"
set /P "=a" < NUL > bitNumberOfChars.txt
rem Identify individual bits in the number of times
rem and append the same number of "a"'s to output file
rem Test 31 bits, from 0 to 30
(for /L %%i in (0,1,30) do if !times! neq 0 (
set /A "bit=times & (1<<%%i), times-=bit"
if !bit! neq 0 type bitNumberOfChars.txt
type bitNumberOfChars.txt >> bitNumberOfChars.txt
)) > output.txt
del bitNumberOfChars.txt
EDIT: Optimized method added
As user dbenham indicated in his comment, this method is not optimized because it uses an auxiliary disk file. The new version below is an optimized one that does not store the data in a file, but in a memory variable as dbenham suggested in his answer. The procedure is the same than in the first method: in each step the string length is doubled and one bit of the given number is tested; if the bit is not zero, the current string is output.
#echo off
setlocal EnableDelayedExpansion
for /F "delims==" %%a in ('set') do set "%%a="
set times=%1
rem Create the initial string with one "a"
set "s=a"
rem Identify individual bits in the number of times
rem and append the same number of "a"'s to output file
< NUL (
rem Test the first 12 bits, from 0 to 11 (string up to 4 KB)
for /L %%i in (0,1,11) do (
set /A "bit=times & (1<<%%i), times-=bit"
if !bit! neq 0 set /P "=!s!"
if !times! equ 0 goto break
set "s=!s!!s!"
)
rem Test the bit 12 (string of 8 KB - 8)
set /A "bit=times & (1<<12), times-=bit"
if !bit! neq 0 set /P "=!s!"
if !times! equ 0 goto break
set "s=!s:~4!"
set "s=!s!!s!"
rem Test the rest of bits, from 13 to 30 (repeating string of 8 KB)
set t2=1, t3=0
for /L %%i in (13,1,30) do if !times! neq 0 (
set /A "bit=times & (1<<%%i), times-=bit"
if !bit! neq 0 (
for /L %%t in (1,1,!t2!) do set /P "=!s!"
set /A "t3+=t2*8"
)
set /A "t2<<=1"
)
rem Add missing bytes (8 bytes per each 8 KB string)
set /A div=t3/8184, mod=t3%%8184
for /L %%t in (1,1,!div!) do set /P "=!s!"
for %%t in (!mod!) do set /P "=!s:~0,%%t!"
) > output.txt
:break
This method have practically the same performance than dbenham's one; however, because this method uses a slightly larger maximum string (8184 vs. 8000 chars.), it will be marginally faster with very large files of certain specific sizes. After completed severals tests and getting the average time, this method ran about 1.5% faster with a file of 10,000,000 characters, and it ran 3.5% faster with a file of 66,000,000 characters.

#echo off
break|set /p=a>file
break|set /p=a>>file
break|set /p=a>>file
break|set /p=a>>file
type file
try this...

<nul set /p"=string"
This is the usual batch construct to output a string without an ending CR/LF.
If you need to "instantly" generate a 10000 a characters to a file, you can use something like
#echo off
setlocal enableextensions disabledelayedexpansion
<nul >"file.txt" (for /l %%a in (1 1 625) do #set /p"=aaaaaaaaaaaaaaaa" )
That is, 16 a * 625 iterations = 10000 a

Unless you are working with a high performance solid state drive, performance will probably be limited by disk write speed. The Aacini solution is not optimized because not only does it write the desired string to disk, it also writes at least that much to a temporary file as well, so the IO cost is roughly doubled.
An optimized solution should minimize disk write operations, both in terms of total length, as well as number of write operations.
The SET /P hack used to write data without carriage return or linefeed (\r\n) is relatively slow. On my machine, ECHO is about 2 times faster than <NUL SET /P. So we also want to minimize the number of times SET /P is executed.
Manipulating strings in memory via variables is comparatively fast.
My optimized solution prepares a variable with a nearly maximum length (8000 out of max possible 8191). The entire big string is written enough times to get close to the desired length, and then a substring operation is used to get the remainder.
I've written two optimized routines to assist with the task.
:MakeString8k replicates any string to a length of exactly 8000 bytes, all within memory, overwriting the original variable. Once defined, this 8k string can be used for multiple write operations.
:WriteBigString uses SET /P to write a string multiple times in a loop, plus a substring, to achieve the desired length. The output is written to stdout, so the CALL can be redirected to the desired output file. This would normally be called with the output of :MakeString8k as the input string, along with the known input length of 8000. If the length of the input string is not passed, then it uses a :strlen function to compute the length.
I've included demo code that shows how to write "a" 10000 times to test.txt. But it is easy to change the parameters to write nearly any string to nearly any given length.
#echo off
setlocal enableDelayedExpansion
set "str=a"
call :makeString8k str
call :writeBigString 10000 str 8000 >>test.txt
exit /b
:MakeString8k StrVar
::
:: Replicate the string within variable StrVar to length 8000.
:: The pattern will be discontinuous at the midway point if 8000
:: is not an even multiple of the original string length.
::
:: If delayed expansion is enabled when called, then the initial
:: string can contain any valid byte code except 0x00. If delayed
:: expansion is disabled when called, then the string cannot contain
:: carriage return (0x0D) or linefeed (0x0A).
::
if "!!" neq "" (
setlocal enableDelayedExpansion
set make8k.setlocal=1
)
for /l %%N in (1 1 13) do set "%~1=!%~1:~0,4000!!%~1:~0,4000!"
if defined make8k.setlocal for /f delims^=^ eol^= %%A in ("!%~1!") do (
endlocal
set "%~1=%%A"
)
exit /b
:WriteBigString Len SeedVar [SeedLen]
::
:: Repeatedly write to stdout the string in seedVar until length Len
:: is reached. Performance is improved slightly if the length of SeedVar
:: is also passed as SeedLen.
::
:: This function may not work properly if the string begins with =, ",
:: or white space.
::
setlocal enableDelayedExpansion
set "seedLen=%~3"
if not defined seedLen call :strlen %2 seedLen
set /a "wholeCnt=%~1/seedLen, subLen=%~1%%seedLen"
<nul (for /l %%N in (1 1 %wholeCnt%) do set /p "=!%~2!")
for %%N in (%subLen%) do <nul set /p "=!%~2:~0,%%N!"
exit /b
:strlen StrVar RtnVar
::
:: Compute the length of the string within StrVar and return
:: the result in variable RtnVar, or write the result to stdout
:: if RtnVar is not specified.
::
setlocal EnableDelayedExpansion
set "s=!%~1!#"
set "len=0"
for %%P in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if "!s:~%%P,1!" NEQ "" (
set /a "len+=%%P"
set "s=!s:~%%P!"
)
)
(
endlocal
if "%~2" neq "" (set "%~2=%len%") else echo %len%
)
exit /b
Note: There are limitations to the SET /P hack - it may not work properly if the string begins with =, ", or a white space character like space, tab, or new line. The exact limitations depend on the version of Windows that you are using.
You could use techniques posted at https://stackoverflow.com/a/19468559/1012053 to adapt this solution to support writing any string other than null (0x00) characters.
I've tested my solution vs. Aacini's solution, and this is about 2 times faster. Both solutions write 10,000 bytes in the blink of an eye. But the difference becomes apparent with large files. It takes Aacini's code ~13 seconds to write 100 million bytes on my machine, whereas my code takes only ~6 seconds.

Related

Converting decimal 100 to 31 30 30 in Batch

I'm trying to convert a number from a text file - lets say number.txt - from decimal 100 to ASCII 31 30 30.
Is there something I can do in batch?
I tried several things I've found on SO already - but never had the right output.
Later after I have converted that - I need to add one number up after some execution. So lets say after 109 / 31 30 39 - it should be 110 31 31 30 and not 31 30 3a for example.
Can you give me a hint?
setlocal enabledelayedexpansion
for /f "tokens=*" %%a in (nummer.txt) do (
set line=%%a
set a=!line:~0,1!
set line=%%a
set b=!line:~1,1!
set line=%%a
set c=!line:~2,1!
set /a mitte=3!a!3!b!3!c!
echo 0F0900000A00143030303030303030303030303030303030!mitte!00>com3
Please see if the following `Rem`arked batch-file example helps you out.
#Echo Off
SetLocal EnableDelayedExpansion
Rem Undefine any existing variable named inputNum.
Set "inputNum="
Rem Read input number from first line of input file.
Set /P "inputNum="<"nummer.txt"
Rem Exit script if inputNum was not defined.
If Not Defined inputNum GoTo :EOF
Rem Define a number of additional iterations [number results required - 1].
Set "#=9"
Rem Define an increment size.
Set "i=5"
Rem Define a variable for total increase, [# x i].
Set /A $=#*i
Rem Undefine an existing variable named arg.
Set "arg="
Rem Begin looping for stated iterations.
For /L %%G In (0,%i%,%$%)Do (
Set /A arg=inputNum+%%G
Rem Call subfunction passing the input number as an argument.
Call :Sub "!arg!"
Rem If successful,
If Defined mitte (
Rem Output resultant number sequence.
Echo !mitte!
) Else (
Rem Exit if no result sequence.
GoTo EndIt
)
)
:EndIt
Rem Allow time to read output before ending.
Pause
EndLocal
GoTo :EOF
:Sub
Rem Undefine any existing variable named mitte.
Set "mitte="
Rem Split each character of passed argument.
For /F Delims^=^ EOL^= %%G In ('"Cmd /U/C Echo(%~1|Find /V """')Do (
Rem prefix each digit of inputNum with 3.
If Not Defined mitte (Set "mitte=3%%G")Else Set "mitte=!mitte!3%%G")
Rem return to next command line after previous Call
Exit /B
For testing, you should only need to ensure that your input file path is correct, (line 6). I have additionally include a facility to output a specific number of additional iterations, (line 10), with a fixed increment, (line 12). Please adjust those as necessary.
What that does if nummer.txt contains only 401, with 9 additional iterations incrementing by 5 should output:
343031
343036
343131
343136
343231
343236
343331
343336
343431
343436
After testing, you'll probably want to replace Echo !mitte! with (Echo 0F0900000A00143030303030303030303030303030303030!mitte!00)>COM3, (line 26)

merge multiple text files, removing all headers and the new header will be changed as follows:

Assuming we have this text files
*0000000000003000345800091600000000000002082019 0
*000000000000322322930002160000000DOU JIJEL 1
*000000000000306156240007000000000TIC TAHER 1
The header contains always what follows :
From position 1 to position 21 we have always this:
*00000000000030003458 which is an «unchangeable» value. It contains 21 characters.
From position 22 to 34, we got 13 characters which represent the sum of the amounts contained in every line the text file from position 22 to 34.
To clarify ; if you look at the header you’ll see from position 22 to 34 :
0009160000000 which is 91 600 000,00
It’s an amount of money, which is the sum of the amounts in the first and second line.
First line : 0002160000000 which is 21 600 000,00
Second line : 0007000000000 which is 70 000 000,00
21 600 000,00+70 000 000,00=91 600 000,00
« If we have in the first line 3162160000000 it means the amount in 31 621 600 000,00
If we have in the first line 0000000541000 it means the amount is 5 410,00 »
From position 35 to 41, we have seven characters, which represent the number of amounts contained in the text file.
We have From position 35 to 41 0000002, and we have two lines except the header, so the sum is 2.
If for example we have 714 lines, the position 35 to 41 in the header will be 0000714, and so on.
So, if I have two text files, and I want to merge them together in one file, in a way that we’ll have:
Only one header and All the lines in the text files.
The lines of course will be unchanged.
But the header will be changed as I explained above, in addition to that and from position 42 to position 62, will always be of the values or the characters contained in the header of the text files I want to merge, which are always the same.
That means that the header will be changed only from position 22 to position 41.
I've managed to remove the headers, but the new header I write it manually
#echo off
setlocal enabledelayedexpansion
if exist output.txt del output.txt
set "var="
for /r %%i in (*.txt) do (
if "%%~nxi" NEQ "output.txt" (
set "var="
for /f "usebackq skip=1 delims=" %%b in ("%%~i") do (
set var=%%b
if "!var!" NEQ "" Echo !var!
))) >> output.txt
this code will remove the header of the text files
So I expect the new header to be calculated automatically
The 32-bits integer values that set /A command can manage only allows to correctly add two numbers up to 9 (decimal) digits, but is very simple to overpass this limitation: just split a large number in two (or more) parts and add them separately. Be aware that when the result of any part exceeds its number of digits, such "overflow digit" (called Carry) must be passed (in the units position) to the next part.
#echo off
setlocal EnableDelayedExpansion
rem Initialize variables to manage first line
rem 01234567890123456789012345678901234567890123456789012345678901
rem /---unchangeable----\/amnt7\/amC6\/lins7\/----unchangeable---\
rem *0000000000003000345800091600000000000002082019 0
set /A amount7=10000000, amountC6=10000000, lines7=10000000
rem Process all files. Group all output to same file
(for /R %%i in (*.txt) do (
rem Input from each file
< "%%i" (
rem Read the first line and accumulate it
set /P header=
set /A amountC6+=10!header:~28,6!
set /A amount7+=1!header:~21,7!+!amountC6:~1,1!, amountC6=10!amountC6:~2!, lines7+=1!header:~34,7!
rem Copy the rest of lines
findstr "^"
)
rem Send all "rest of lines" to temporary file
)) > output.tmp
rem Add result header to temporary file, and delete it
(
echo %header:~0,21%%amount7:~1%%amountC6:~2%%lines7:~1%%header:~41%
type output.tmp
) > output.txt
del output.tmp
#ECHO OFF
setlocal enabledelayedexpansion
if exist output.txt del output.txt
set "headerproduced="
set "header="
set "var="
:pass2
for /r %%i in (*.txt) do (
if "%%~nxi" NEQ "output.txt" (
set "var="
if defined headerproduced (
if !headerproduced!==0 set /a headerproduced=1&echo !header!
for /f "usebackq skip=1 delims=" %%b in ("%%~i") do (
set "var=%%b"
if "!var!" NEQ "" Echo !var!
)
) else (
rem header not yet produced
for /f "usebackq delims=" %%b in ("%%~i") do if not defined var (
if defined header (
rem subsequent headers - accumulate
set "var=%%b"
set /a var1=1!header:~34,7! + 1!var:~34,7!
set /a var2=1!header:~28,6! + 1!var:~28,6!
set /a var3=1!header:~21,7! + 1!var:~21,7!
if !var2! geq 3000000 set /a var3+=1
set "header=!header:~0,21!!var3:~-7!!var2:~-6!!var1:~-7!!header:~41!"
) else (
rem very first header
set "header=%%b"
set "var=1"
)
)
)
)
) >> output.txt
if not defined headerproduced set /a headerproduced=0&goto pass2
GOTO :EOF
The syntax SET "var=value" (where value may be empty) is used to ensure that any stray trailing spaces are NOT included in the value assigned. In your posted code, there appears to be trailing spaces on lines - especially the set var=%%b which would generate 4 extra spaces at the end of each dataline.
This code works by using header to contain the new header for the file.
At first, no header has yet been encountered. When a file is read, var is set to nothing and each line of the file is processed.
When the first file is read, if defined header is FALSE so the header line is recorded in header and var set to some value so that it is defined. Subsequent lines of the file are ignored courtesy of the if not defined var gate.
On reading the remaining files, header is now defined, so we need to accumulate data. The accumulated data is assigned back to header in the appropriate spots. We then need to deal with adding the two fields. This is where we encounter batch's quirky maths.
Batch uses 32-bit signed-integers for its maths operations. That's fine for the shorter number, but the longer needs to be split into two - I chose the leading 7 digits and the trailing 6 digits.
Next minor matter is that batch regards a numeric string starting 0 as Octal, so we simply poke a 1 in front of each, add them up and use the last 6 or 7 digits of the result. In the case of the 6-digit portion, we can have an overflow - 1999998 + 1000003 = 3000001 - 3000000 or greater means we have overflow and need to increment the 7-digit portion.
Once all of the files have been read, header contains the required value but no header has yet been generated. We return to pass2 having set headerproduced to a significant value.
On the second pass, headerproduced now has a value. If that value is 0, we echo out the headerline and alter headerproduced to prevent multiple accumulated-headerlines being produced.
After that, output each line bar the first of each file as before.

Update running unsigned long long sum in bat file

Is there a simple way to sum two numbers potentially >= 2*31 in a .BAT file?
I have a running sum, and argument %1 that is the name of an existing file.
set sum=4123456789
set fsize=%~z1
I'd like to add fsize to sum. Unfortunately fsize (and sum) can be as tiny as zero or 10's of gigabytes (%~z1 accurately reports >= 2*31 file sizes).
I know a program could do it, and I'll go that route if necessary, but I'd prefer to do it with a few added lines of .BAT logic.
I think the easiest way is to split the summands into two parts – integer and fractional Gigas (multiples of 1000000000), add the respective parts individually, then recombine them. See the following example script, which contains a lot of explanatory remarks:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Initialise variables:
set /A "GIGASUM=0, FRACSUM=0"
:LOOP
rem // Get command line argument:
shift
set "NUM=%~0"
if not defined NUM goto :NEXT
rem // Check number for validity:
(for /F "delims=0123456789" %%N in ("%NUM%") do rem/) && (
echo ERROR: non-numeric characters encountered!
exit /B 1
)
rem // Split number into two parts, integer and fractional Gigas:
set "NUM=000000000%NUM%"
set "GIGA=%NUM:~,-9%" & set "FRAC=%NUM:~-9%"
rem // Remove leading zeros from integer Gigas:
for /F "tokens=* delims=0" %%N in ("%GIGA%") do set "GIGA=%%N"
rem // Sum up fractional Gigas and determine carry:
set /A "FRACSUM+=(1%FRAC%-1000000000)"
set "CARRY=%FRACSUM:~,-9%" & set "FRACSUM=000000000%FRACSUM%"
set "FRACSUM=%FRACSUM:~-9%"
rem // Sum up integer Gigas and regard carry:
set /A "GIGASUM+=GIGA+CARRY"
rem // Loop back to next summand:
goto :LOOP
:NEXT
rem // Remove leading zeros:
for /F "tokens=* delims=0" %%N in ("%GIGASUM%%FRACSUM%") do set "SUM=%%N"
if not defined SUM set "SUM=0"
rem // Return resulting sum:
echo %SUM%
endlocal
exit /B
The greatest possible sum amounts to 231 * 109 – 1 = 2147483647999999999, an overflow is not detected.
The required logic is not so complicated. Here it is one version:
#echo off
setlocal
:loop
set /p "pair=Enter two numbers separated by plus sign: "
if errorlevel 1 goto :EOF
for /F "tokens=1,2 delims=+" %%a in ("%pair%") do set "num1=%%a" & set "num2=%%b"
set "sum="
set "carry=0"
:nextDigit
set /A sum1=%num1:~-1%+%num2:~-1%+carry
set "sum=%sum1:~-1%%sum%"
set /A carry=sum1/10
set "num1=%num1:~0,-1%"
if not defined num1 set "num1=0"
set "num2=%num2:~0,-1%"
if not defined num2 set "num2=0"
if "%carry%%num1%%num2%" neq "000" goto nextDigit
echo The sum is: %sum%
goto loop

How do I add leading zeros to an image sequence in Windows Batch?

I found this link that explains how to add leading zeros to a sequence of numbers:
#echo off
set count=5
setlocal EnableDelayedExpansion
for /L %%i in (1, 1, %count%) do (
set "formattedValue=000000%%i"
echo !formattedValue:~-6!
)
This outputs
000001
000002
000003
000004
000005
I have image sequences that look like this:
%1.j2c_0.j2c
%1.j2c_10.j2c
%1.j2c_100000.j2c
I would like to always have 6 numbers:
%1.j2c_000000.j2c
%1.j2c_000010.j2c
%1.j2c_100000.j2c
The file name before the last underscore can change and include more underscores.
So I guess I need to find out how many numbers are to the right of the last underscore and add the correct number of zeros to get 6 numbers.
How do I do that?
As far as I understood your question, the pattern for the image files is *_*.j2c.
Since the leading file name part may contain multiple _, a trick can be used: for provides the possibility to split iterated items into pieces by giving ~ modifiers; supposing the variable is %I, a %~nI returns the file name, %~xI the extension, %~nxI file name plus extension (see for /?).
If no wildcards *, ? are used, splitting is done without accessing the file system; so we can use this for string manipulation operations. In order to apply this to our task at hand, we need to replace every _ by the path separator \.
There are two nested for loops; the outer one iterates through all the image files, and the inner one receives each file name plus ext. from the outer and iterates just once, where the path string splitting feature is (mis-)used for our needs.
The following code snippet uses the described trick to extracted the part after the last _ of the file name, which is the number to be padded with 6 leading zeros then. See also the explanatory rem comments:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "IMAGES=*_*.j2c"
set "DIGITS=6" & rem add more padding `0`s below if DIGITS > 12!
rem loop through all images that match the pattern given above;
rem since there is a wildcard `*`, the file system is accessed:
for %%I in ("%IMAGES%") do (
rem now let us extract the portion between the last `_` and the ext.;
rem for this we replace every `_` by `\`, so the item looks like a file path;
rem then we split off the file name portion of the path-like string;
rem since there are no more wildcards, the file system is NOT accessed:
set "ARG=%%~nxI"
set "FPTH=%%~fI"
setlocal EnableDelayedExpansion
for %%F in ("!ARG:_=\!") do (
rem extract and pad the numeric file name portion:
set "VAL=000000000000%%~nF"
set "VAL=!VAL:~-%DIGITS%!"
rem rebuild file name; the `|` ensures that only the part
rem after the last `_` is replaced by the padded number:
setlocal DisableDelayedExpansion
set "ARG=%%~F|"
setlocal EnableDelayedExpansion
set "ARG=!ARG:%%~nxF|=!!VAL!%%~xF"
)
ren "!FPTH!" "!ARG:\=_!"
endlocal
endlocal
endlocal
)
endlocal
The great advantage of this method is that we do not need to know the number of _ characters (opposed to something like for /F "tokens=1,2 delims=_", where the number of tokens is fixed).
this was a little bit challenging (but a little) :
#echo off
setlocal enableDelayedExpansion
:: path to the directory with the files
set "files_dir=."
pushd "%files_dir%"
for %%# in (*_*j2c) do (
set "filename=%%~nx#"
for /f "tokens=1,2,3,4 delims=_." %%A in ("!filename!") do (
set "num=%%C"
call ::strlen0 num numlen
if !numlen! LSS 6 (
for /l %%] in (!numlen! ; 1 ; 6 ) do (
set "num=0!num!"
)
ren %%A.%%B_%%C.%%D %%A.%%B_!num!.%%D
) else (
echo %%# will be not processed - more than or 6 numbers
)
)
)
endlocal & (
popd
exit /b %errorlevel%
)
:: taken from http://ss64.org/viewtopic.php?id=424
:strlen0 StrVar [RtnVar]
setlocal EnableDelayedExpansion
set "s=#!%~1!"
set "len=0"
for %%N in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if "!s:~%%N,1!" neq "" (
set /a "len+=%%N"
set "s=!s:~%%N!"
)
)
endlocal&if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b

Batch: count the number of digits in a variable

I want to find a way to know the number of digits in variable. For now, I'm trying to use this code. In this example, %var% is the variable that I need to know the number of digits it has.
set x=1
set var=12345
:LOOP
set temp=%var:~0,%x%%
if %temp%==%var% goto END
set x=%x%+1
goto LOOP
:END
Theoretically, at the end of the code %x% would be the number of digits %var% has. However, it doesn't work. I found out the problem is at the 3rd line. I modified the code to diagnose:
set x=1
set var=12345
:LOOP
set temp=%var:~0,%x%%
echo %temp%
pause
if %temp%==%var% goto END
set x=%x%+1
goto LOOP
:END
The result echoed was:
x%%
Can anyone pinpoint my mistake or give an alternative solution to determine the number of digits in a variable?
Here's a short solution, that only works for numeric variables:
set /a Log=1%var:~1%-%var:~1% -0
set /a Len=%Log:0=+1%
The variable %Len% will contain the number of digits in %var%.
Explanation
The basic idea is to convert the first digit to 1, and the rest of them (the 'trailing' digits) to 0's. Then we can use the string replacement function to replace all the 0's with +1 giving us 1+1+1.... and evaluate the string as an arithmetic expression. This will give us the total number of digits.
The 'trailing' digits can be gotten using %var:~1% and we convert them to 0 by subtracting them from the variable itself: 45678 - 5678 gives 40000 etc. However, the above code subtracts them from 1%var:~1% instead, in order to replace the first digit with 1 (i.e. 1 followed by the 'trailing' digits).
The reason for the extra -0 is in case %var% only has one digit, for example 7. In that case, the expression 1%var:~1%-%var:~1% would evaluate to 1- and the shell would complain: Missing operand. The -0 ensures that we always have a valid expression.
Now that we've converted the variable in to the proper form into %Log%, we can replace every occurrence of 0 with +1using %Log:0=+1% and evaluate the resulting expression using set /a, giving us our final result.
The main problem with your code is
set temp=%var:~0,%x%%
This does not work. The parser is not able to properly determine what percent sign belongs to what variable. You can enable delayed expansion and write it as
set "temp=!var:~0,%x%!"
For alternative versions, to handle any length string, any of the posted answers will work.
For a simpler solution, if you are sure the string is under 10 characters, then this is an alternative
set "x=0123456789%var%"
set "x=%x:~-10,1%"
As there is no build in function for string length, you can write your own function.
#echo off
setlocal
set "myString=abcdef!%%^^()^!"
call :strlen result myString
echo %result%
goto :eof
:strlen <resultVar> <stringVar>
(
setlocal EnableDelayedExpansion
set "s=!%~2!#"
set "len=0"
for %%P in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if "!s:~%%P,1!" NEQ "" (
set /a "len+=%%P"
set "s=!s:~%%P!"
)
)
)
(
endlocal
set "%~1=%len%"
exit /b
)
This function needs always 13 loops, instead of a simple strlen function which needs strlen-loops.
It handles all characters.
Source: How do you get the string length in a batch file?
Your are trying to do this loop :
#Echo Off
Set /P VrStr=Enter your string :
:Loop
If "%VrStr%" EQU "" Goto EndLoop
Set VrStr=%VrStr:~0,-1%
Set /A VrLgr+=1
Goto Loop
:EndLoop
Echo Number of char: %VrLgr%
Pause
You can use this to :
#echo off
setlocal EnableDelayedExpansion
Set /P $Tstring=Enter your string:
for /l %%a in (0,1,9000) do (set $t=!$Tstring:~%%a,1!&if not defined $t (echo [NB OF CHAR =] %%a&pause&exit /b))
pause

Resources