【问题标题】:Append Text with batch file用批处理文件附加文本
【发布时间】:2015-08-09 00:09:32
【问题描述】:

我使用以下代码使用批处理文件将a 5 次写入hi.txt

问题是它会自动在末尾添加换行符。

输出:

a
a
a
a
a

我想要:

aaaaa

【问题讨论】:

  • 我也想这样做 10,000 次,有什么办法让它立即发生?

标签: batch-file window


【解决方案1】:

下面的方法可能是创建具有给定数量的相同字符的文件的最快方法。如果数字只有 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%。

【讨论】:

  • 谢谢!它的工作很酷,但请告诉我在哪里更改数字(10,000 到 20,000)?
  • 如果您选择我的答案作为最佳答案,我会告诉您,只需将所需的数字放入时间变量中,即:set times=20000 而不是 set times=10000
  • 实际上,这并没有优化,因为它使写入磁盘的总内容增加了一倍以上,而磁盘 IO 通常是性能瓶颈。
【解决方案2】:
@echo off
break|set /p=a>file
break|set /p=a>>file
break|set /p=a>>file 
break|set /p=a>>file
type file

试试这个...

【讨论】:

    【解决方案3】:
    <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

    【讨论】:

      【解决方案4】:

      除非您使用高性能固态驱动器,否则性能可能会受到磁盘写入速度的限制。 Aacini 解决方案没有进行优化,因为它不仅将所需的字符串写入磁盘,而且还将至少那么多的内容写入临时文件,因此 IO 成本大约增加了一倍。

      优化的解决方案应尽量减少磁盘写入操作,无论是总长度还是写入操作次数。

      用于在没有回车或换行 (\r\n) 的情况下写入数据的 SET /P hack 相对较慢。在我的机器上,ECHO&lt;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 秒。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-10-18
        • 1970-01-01
        • 1970-01-01
        • 2017-11-08
        • 1970-01-01
        • 1970-01-01
        • 2019-08-16
        • 2011-05-26
        相关资源
        最近更新 更多