用批处理文件追加文本
Append Text with batch file
我使用以下代码使用批处理文件将 a
写入 hi.txt
5 次。
问题是它会自动在末尾附加换行符。
输出:
a
a
a
a
a
我要:
aaaaa
@echo off
break|set /p=a>file
break|set /p=a>>file
break|set /p=a>>file
break|set /p=a>>file
type file
试试这个...
<nul set /p"=string"
这是输出没有结尾的字符串的常用批处理构造 CR/LF。
如果你需要"instantly" 生成一个 10000 a
个字符到一个文件,你可以使用像
@echo off
setlocal enableextensions disabledelayedexpansion
<nul >"file.txt" (for /l %%a in (1 1 625) do @set /p"=aaaaaaaaaaaaaaaa" )
即16 a * 625 iterations = 10000 a
下面的方法可能是创建具有给定数量的相同字符的文件的最快方法。如果数量只有 10,000,文件会立即创建。
@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
编辑:添加了优化方法
正如用户 dbenham 在他的评论中指出的那样,此方法未优化,因为它使用了辅助磁盘文件。下面的新版本是一个优化版本,它不将数据存储在文件中,而是存储在 dbenham 在他的回答中建议的内存变量中。过程与第一种方法相同:在每一步中将字符串长度加倍,并测试给定数字的一位;如果该位不为零,则输出当前字符串。
@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
该方法与dbenham 的方法几乎具有相同的性能;但是,由于此方法使用稍大的最大字符串(8184 与 8000 个字符),因此对于某些特定大小的非常大的文件,它会稍快一些。在完成几次测试并获得平均时间后,此方法在处理 10,000,000 个字符的文件时 运行 快了大约 1.5%,在处理 66,000,000 个字符的文件时 运行 快了 3.5%。
除非您使用的是高性能固态驱动器,否则性能可能会受到磁盘写入速度的限制。 Aacini 解决方案没有优化,因为它不仅将所需的字符串写入磁盘,而且还至少将同样多的内容写入临时文件,因此 IO 成本大约翻了一番。
优化的解决方案应尽量减少磁盘写入操作,包括总长度和写入操作次数。
用于不带回车符 return 或换行符 (\r\n) 写入数据的 SET /P hack 相对较慢。在我的机器上,ECHO
比 <NUL SET /P
快 2 倍左右。所以我们也想尽量减少SET /P的执行次数。
通过变量操作内存中的字符串比较快。
我的优化解决方案准备了一个几乎最大长度的变量(最大可能的 8191 中的 8000)。整个大串写足够多的次数接近想要的长度,然后用子串运算求余数。
我编写了两个优化例程来协助完成任务。
:MakeString8k
将任何字符串复制到恰好 8000 字节的长度,全部在内存中,覆盖原始变量。一旦定义,这个8k的字符串可以用于多次写操作。
:WriteBigString
使用SET /P循环多次写入一个字符串,加上一个子串,达到想要的长度。输出被写入标准输出,因此 CALL 可以重定向到所需的输出文件。这通常会使用 :MakeString8k
的输出作为输入字符串以及已知的输入长度 8000 来调用。如果未传递输入字符串的长度,则它使用 :strlen
函数来计算长度。
我已经包含演示代码,演示如何将 "a" 写入 test.txt 10000 次。但是很容易更改参数以将几乎任何字符串写入几乎任何给定长度。
@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
注意:There are limitations to the SET /P hack - 如果字符串以 =
、"
或 space 之类的白色 space 字符开头,则可能无法正常工作、制表符或新行。确切的限制取决于您使用的 Windows 版本。
您可以使用 上发布的技术来调整此解决方案以支持写入空 (0x00) 字符以外的任何字符串。
我已经测试了我的解决方案与 Aacini 的解决方案,这大约快 2 倍。两种解决方案都在眨眼间写入 10,000 个字节。但是对于大文件,差异变得明显。 Aacini 的代码在我的机器上写入 1 亿字节需要大约 13 秒,而我的代码只需要大约 6 秒。
我使用以下代码使用批处理文件将 a
写入 hi.txt
5 次。
问题是它会自动在末尾附加换行符。
输出:
a
a
a
a
a
我要:
aaaaa
@echo off
break|set /p=a>file
break|set /p=a>>file
break|set /p=a>>file
break|set /p=a>>file
type file
试试这个...
<nul set /p"=string"
这是输出没有结尾的字符串的常用批处理构造 CR/LF。
如果你需要"instantly" 生成一个 10000 a
个字符到一个文件,你可以使用像
@echo off
setlocal enableextensions disabledelayedexpansion
<nul >"file.txt" (for /l %%a in (1 1 625) do @set /p"=aaaaaaaaaaaaaaaa" )
即16 a * 625 iterations = 10000 a
下面的方法可能是创建具有给定数量的相同字符的文件的最快方法。如果数量只有 10,000,文件会立即创建。
@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
编辑:添加了优化方法
正如用户 dbenham 在他的评论中指出的那样,此方法未优化,因为它使用了辅助磁盘文件。下面的新版本是一个优化版本,它不将数据存储在文件中,而是存储在 dbenham 在他的回答中建议的内存变量中。过程与第一种方法相同:在每一步中将字符串长度加倍,并测试给定数字的一位;如果该位不为零,则输出当前字符串。
@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
该方法与dbenham 的方法几乎具有相同的性能;但是,由于此方法使用稍大的最大字符串(8184 与 8000 个字符),因此对于某些特定大小的非常大的文件,它会稍快一些。在完成几次测试并获得平均时间后,此方法在处理 10,000,000 个字符的文件时 运行 快了大约 1.5%,在处理 66,000,000 个字符的文件时 运行 快了 3.5%。
除非您使用的是高性能固态驱动器,否则性能可能会受到磁盘写入速度的限制。 Aacini 解决方案没有优化,因为它不仅将所需的字符串写入磁盘,而且还至少将同样多的内容写入临时文件,因此 IO 成本大约翻了一番。
优化的解决方案应尽量减少磁盘写入操作,包括总长度和写入操作次数。
用于不带回车符 return 或换行符 (\r\n) 写入数据的 SET /P hack 相对较慢。在我的机器上,ECHO
比 <NUL SET /P
快 2 倍左右。所以我们也想尽量减少SET /P的执行次数。
通过变量操作内存中的字符串比较快。
我的优化解决方案准备了一个几乎最大长度的变量(最大可能的 8191 中的 8000)。整个大串写足够多的次数接近想要的长度,然后用子串运算求余数。
我编写了两个优化例程来协助完成任务。
:MakeString8k
将任何字符串复制到恰好 8000 字节的长度,全部在内存中,覆盖原始变量。一旦定义,这个8k的字符串可以用于多次写操作。
:WriteBigString
使用SET /P循环多次写入一个字符串,加上一个子串,达到想要的长度。输出被写入标准输出,因此 CALL 可以重定向到所需的输出文件。这通常会使用 :MakeString8k
的输出作为输入字符串以及已知的输入长度 8000 来调用。如果未传递输入字符串的长度,则它使用 :strlen
函数来计算长度。
我已经包含演示代码,演示如何将 "a" 写入 test.txt 10000 次。但是很容易更改参数以将几乎任何字符串写入几乎任何给定长度。
@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
注意:There are limitations to the SET /P hack - 如果字符串以 =
、"
或 space 之类的白色 space 字符开头,则可能无法正常工作、制表符或新行。确切的限制取决于您使用的 Windows 版本。
您可以使用 上发布的技术来调整此解决方案以支持写入空 (0x00) 字符以外的任何字符串。
我已经测试了我的解决方案与 Aacini 的解决方案,这大约快 2 倍。两种解决方案都在眨眼间写入 10,000 个字节。但是对于大文件,差异变得明显。 Aacini 的代码在我的机器上写入 1 亿字节需要大约 13 秒,而我的代码只需要大约 6 秒。