【发布时间】:2015-08-09 00:09:32
【问题描述】:
我使用以下代码使用批处理文件将a 5 次写入hi.txt。
问题是它会自动在末尾添加换行符。
输出:
a
a
a
a
a
我想要:
aaaaa
【问题讨论】:
-
我也想这样做 10,000 次,有什么办法让它立即发生?
标签: batch-file window
我使用以下代码使用批处理文件将a 5 次写入hi.txt。
问题是它会自动在末尾添加换行符。
输出:
a
a
a
a
a
我想要:
aaaaa
【问题讨论】:
标签: batch-file window
下面的方法可能是创建具有给定数量的相同字符的文件的最快方法。如果数字只有 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%。
【讨论】:
set times=20000 而不是 set times=10000。
@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 的字符串的常用批处理构造。
如果您需要“立即”为文件生成 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
【讨论】:
除非您使用高性能固态驱动器,否则性能可能会受到磁盘写入速度的限制。 Aacini 解决方案没有进行优化,因为它不仅将所需的字符串写入磁盘,而且还将至少那么多的内容写入临时文件,因此 IO 成本大约增加了一倍。
优化的解决方案应尽量减少磁盘写入操作,无论是总长度还是写入操作次数。
用于在没有回车或换行 (\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 - 如果字符串以 =、" 或空格字符、制表符或换行符等空格字符开头,它可能无法正常工作。具体限制取决于您使用的 Windows 版本。
您可以使用https://stackoverflow.com/a/19468559/1012053 发布的技术来调整此解决方案,以支持写入除空 (0x00) 字符以外的任何字符串。
我已经测试了我的解决方案与 Aacini 的解决方案,这大约快了 2 倍。两种解决方案都在眨眼间写入了 10,000 个字节。但是对于大文件,差异变得明显。 Aacini 的代码在我的机器上写入 1 亿字节大约需要 13 秒,而我的代码只需要大约 6 秒。
【讨论】: