1

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
Ashesh
  • 71
  • 2
  • 10

4 Answers4

1
@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...

npocmaka
  • 55,367
  • 18
  • 148
  • 187
1
<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

MC ND
  • 69,615
  • 8
  • 84
  • 126
1

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.

Aacini
  • 65,180
  • 12
  • 72
  • 108
  • Thanks! its working cool but please tell me where to chnage rhe number (10,000 to 20,000 ) ? – Ashesh May 28 '15 at 11:33
  • If you select my answer as Best Answer, I would tell you that just put the desired number in times variable, i.e.: `set times=20000` instead of `set times=10000`. – Aacini May 28 '15 at 21:41
  • Actually, this is not optimized because it more than doubles the total content that is written to disk, and disk IO is typically a performance bottleneck. – dbenham May 30 '15 at 03:41
0

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.

Community
  • 1
  • 1
dbenham
  • 127,446
  • 28
  • 251
  • 390