【问题标题】:How can I split a text file using PowerShell?如何使用 PowerShell 拆分文本文件?
【发布时间】:2010-11-03 08:37:45
【问题描述】:

我需要将一个大的 (500 MB) 文本文件(一个 log4net 异常文件)拆分为可管理的块,例如 100 个 5 MB 文件就可以了。

我认为这应该是 PowerShell 的公园散步。我该怎么做?

【问题讨论】:

    标签: powershell


    【解决方案1】:

    这对 PowerShell 来说有点简单,但由于标准 Get-Content cmdlet 不能很好地处理非常大的文件这一事实而变得复杂。我建议做的是使用 .NET StreamReader class 在 PowerShell 脚本中逐行读取文件,并使用 Add-Content cmdlet 将每一行写入文件名中索引不断增加的文件。像这样的:

    $upperBound = 50MB # calculated by Powershell
    $ext = "log"
    $rootName = "log_"
    
    $reader = new-object System.IO.StreamReader("C:\Exceptions.log")
    $count = 1
    $fileName = "{0}{1}.{2}" -f ($rootName, $count, $ext)
    while(($line = $reader.ReadLine()) -ne $null)
    {
        Add-Content -path $fileName -value $line
        if((Get-ChildItem -path $fileName).Length -ge $upperBound)
        {
            ++$count
            $fileName = "{0}{1}.{2}" -f ($rootName, $count, $ext)
        }
    }
    
    $reader.Close()
    

    【讨论】:

    • 这正是我想要的,感谢您确认我的预感,即 get-content 不适用于大文件。
    • 实用提示:你可以这样表达数字... $upperBound = 5MB
    • 对于那些懒得看下一个答案的人,可以通过 $reader = new-object System.IO.StreamReader($inputFile) 设置 $reader 对象
    • 我建议在调用 add-content 写入内容之前使用 stringbuilder 连接各个行,否则这种方法非常慢。
    • @CVertex 您确实意识到您的脚本首先将整个文件读入内存?所以这对于一个真正巨大的文件(多 GB)永远不会起作用。
    【解决方案2】:

    我经常需要做同样的事情。诀窍是将标头重复到每个拆分块中。我编写了以下 cmdlet (PowerShell v2 CTP 3),它成功了。

    ##############################################################################
    #.SYNOPSIS
    # Breaks a text file into multiple text files in a destination, where each
    # file contains a maximum number of lines.
    #
    #.DESCRIPTION
    # When working with files that have a header, it is often desirable to have
    # the header information repeated in all of the split files. Split-File
    # supports this functionality with the -rc (RepeatCount) parameter.
    #
    #.PARAMETER Path
    # Specifies the path to an item. Wildcards are permitted.
    #
    #.PARAMETER LiteralPath
    # Specifies the path to an item. Unlike Path, the value of LiteralPath is
    # used exactly as it is typed. No characters are interpreted as wildcards.
    # If the path includes escape characters, enclose it in single quotation marks.
    # Single quotation marks tell Windows PowerShell not to interpret any
    # characters as escape sequences.
    #
    #.PARAMETER Destination
    # (Or -d) The location in which to place the chunked output files.
    #
    #.PARAMETER Count
    # (Or -c) The maximum number of lines in each file.
    #
    #.PARAMETER RepeatCount
    # (Or -rc) Specifies the number of "header" lines from the input file that will
    # be repeated in each output file. Typically this is 0 or 1 but it can be any
    # number of lines.
    #
    #.EXAMPLE
    # Split-File bigfile.csv 3000 -rc 1
    #
    #.LINK 
    # Out-TempFile
    ##############################################################################
    function Split-File {
    
        [CmdletBinding(DefaultParameterSetName='Path')]
        param(
    
            [Parameter(ParameterSetName='Path', Position=1, Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
            [String[]]$Path,
    
            [Alias("PSPath")]
            [Parameter(ParameterSetName='LiteralPath', Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
            [String[]]$LiteralPath,
    
            [Alias('c')]
            [Parameter(Position=2,Mandatory=$true)]
            [Int32]$Count,
    
            [Alias('d')]
            [Parameter(Position=3)]
            [String]$Destination='.',
    
            [Alias('rc')]
            [Parameter()]
            [Int32]$RepeatCount
    
        )
    
        process {
    
            # yeah! the cmdlet supports wildcards
            if ($LiteralPath) { $ResolveArgs = @{LiteralPath=$LiteralPath} }
            elseif ($Path) { $ResolveArgs = @{Path=$Path} }
    
            Resolve-Path @ResolveArgs | %{
    
                $InputName = [IO.Path]::GetFileNameWithoutExtension($_)
                $InputExt  = [IO.Path]::GetExtension($_)
    
                if ($RepeatCount) { $Header = Get-Content $_ -TotalCount:$RepeatCount }
    
                # get the input file in manageable chunks
    
                $Part = 1
                Get-Content $_ -ReadCount:$Count | %{
    
                    # make an output filename with a suffix
                    $OutputFile = Join-Path $Destination ('{0}-{1:0000}{2}' -f ($InputName,$Part,$InputExt))
    
                    # In the first iteration the header will be
                    # copied to the output file as usual
                    # on subsequent iterations we have to do it
                    if ($RepeatCount -and $Part -gt 1) {
                        Set-Content $OutputFile $Header
                    }
    
                    # write this chunk to the output file
                    Write-Host "Writing $OutputFile"
                    Add-Content $OutputFile $_
    
                    $Part += 1
    
                }
    
            }
    
        }
    
    }
    

    【讨论】:

    • 效果很好。当您希望每个文件有更多行时,可能希望将 count 变为 long。此外,如果您编写大文件,此脚本会耗尽内存。
    • 非常方便地将一个简单的单列服务器名称文本文件拆分为多个用于批处理。
    • @Josh 我试过你的方法.. 我在哪里得到这个结果Creating file number 1 Reading 500 Closing file Creating file number 2 Reading 500 Closing file Creating file number 3 .... Creating file number 13 Reading 500 Split complete in 3.419523 seconds 但我找不到文件的创建位置?
    【解决方案3】:

    我对根据每个部分的大小拆分文件进行了一些修改。

    ##############################################################################
    #.SYNOPSIS
    # Breaks a text file into multiple text files in a destination, where each
    # file contains a maximum number of lines.
    #
    #.DESCRIPTION
    # When working with files that have a header, it is often desirable to have
    # the header information repeated in all of the split files. Split-File
    # supports this functionality with the -rc (RepeatCount) parameter.
    #
    #.PARAMETER Path
    # Specifies the path to an item. Wildcards are permitted.
    #
    #.PARAMETER LiteralPath
    # Specifies the path to an item. Unlike Path, the value of LiteralPath is
    # used exactly as it is typed. No characters are interpreted as wildcards.
    # If the path includes escape characters, enclose it in single quotation marks.
    # Single quotation marks tell Windows PowerShell not to interpret any
    # characters as escape sequences.
    #
    #.PARAMETER Destination
    # (Or -d) The location in which to place the chunked output files.
    #
    #.PARAMETER Size
    # (Or -s) The maximum size of each file. Size must be expressed in MB.
    #
    #.PARAMETER RepeatCount
    # (Or -rc) Specifies the number of "header" lines from the input file that will
    # be repeated in each output file. Typically this is 0 or 1 but it can be any
    # number of lines.
    #
    #.EXAMPLE
    # Split-File bigfile.csv -s 20 -rc 1
    #
    #.LINK 
    # Out-TempFile
    ##############################################################################
    function Split-File {
    
        [CmdletBinding(DefaultParameterSetName='Path')]
        param(
    
            [Parameter(ParameterSetName='Path', Position=1, Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
            [String[]]$Path,
    
            [Alias("PSPath")]
            [Parameter(ParameterSetName='LiteralPath', Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
            [String[]]$LiteralPath,
    
            [Alias('s')]
            [Parameter(Position=2,Mandatory=$true)]
            [Int32]$Size,
    
            [Alias('d')]
            [Parameter(Position=3)]
            [String]$Destination='.',
    
            [Alias('rc')]
            [Parameter()]
            [Int32]$RepeatCount
    
        )
    
        process {
    
      # yeah! the cmdlet supports wildcards
            if ($LiteralPath) { $ResolveArgs = @{LiteralPath=$LiteralPath} }
            elseif ($Path) { $ResolveArgs = @{Path=$Path} }
    
            Resolve-Path @ResolveArgs | %{
    
                $InputName = [IO.Path]::GetFileNameWithoutExtension($_)
                $InputExt  = [IO.Path]::GetExtension($_)
    
                if ($RepeatCount) { $Header = Get-Content $_ -TotalCount:$RepeatCount }
    
       Resolve-Path @ResolveArgs | %{
    
        $InputName = [IO.Path]::GetFileNameWithoutExtension($_)
        $InputExt  = [IO.Path]::GetExtension($_)
    
        if ($RepeatCount) { $Header = Get-Content $_ -TotalCount:$RepeatCount }
    
        # get the input file in manageable chunks
    
        $Part = 1
        $buffer = ""
        Get-Content $_ -ReadCount:1 | %{
    
         # make an output filename with a suffix
         $OutputFile = Join-Path $Destination ('{0}-{1:0000}{2}' -f ($InputName,$Part,$InputExt))
    
         # In the first iteration the header will be
         # copied to the output file as usual
         # on subsequent iterations we have to do it
         if ($RepeatCount -and $Part -gt 1) {
          Set-Content $OutputFile $Header
         }
    
         # test buffer size and dump data only if buffer is greater than size
         if ($buffer.length -gt ($Size * 1MB)) {
          # write this chunk to the output file
          Write-Host "Writing $OutputFile"
          Add-Content $OutputFile $buffer
          $Part += 1
          $buffer = ""
         } else {
          $buffer += $_ + "`r"
         }
        }
       }
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

      我在尝试将单个 vCard VCF 文件中的多个联系人拆分为单独的文件时发现了这个问题。这是我根据 Lee 的代码所做的。我不得不查找如何创建一个新的 StreamReader 对象并将 null 更改为 $null。

      $reader = new-object System.IO.StreamReader("C:\Contacts.vcf")
      $count = 1
      $filename = "C:\Contacts\{0}.vcf" -f ($count) 
      
      while(($line = $reader.ReadLine()) -ne $null)
      {
          Add-Content -path $fileName -value $line
      
          if($line -eq "END:VCARD")
          {
              ++$count
              $filename = "C:\Contacts\{0}.vcf" -f ($count)
          }
      }
      
      $reader.Close()
      

      【讨论】:

        【解决方案5】:

        关于一些现有答案的警告 - 对于非常大的文件,它们的运行速度会非常慢。对于一个 1.6 GB 的日志文件,我在几个小时后放弃了,意识到在我第二天回去工作之前它不会完成。

        两个问题:对Add-Content 的调用打开、查找然后关闭源文件中每一行的当前目标文件。每次读取一点源文件并查找新行也会减慢速度,但我猜是 Add-Content 是罪魁祸首。

        以下变体产生的输出稍微不那么令人愉快:它会在行的中间拆分文件,但它会在不到一分钟的时间内拆分我的 1.6 GB 日志:

        $from = "C:\temp\large_log.txt"
        $rootName = "C:\temp\large_log_chunk"
        $ext = "txt"
        $upperBound = 100MB
        
        
        $fromFile = [io.file]::OpenRead($from)
        $buff = new-object byte[] $upperBound
        $count = $idx = 0
        try {
            do {
                "Reading $upperBound"
                $count = $fromFile.Read($buff, 0, $buff.Length)
                if ($count -gt 0) {
                    $to = "{0}.{1}.{2}" -f ($rootName, $idx, $ext)
                    $toFile = [io.file]::OpenWrite($to)
                    try {
                        "Writing $count to $to"
                        $tofile.Write($buff, 0, $count)
                    } finally {
                        $tofile.Close()
                    }
                }
                $idx ++
            } while ($count -gt 0)
        }
        finally {
            $fromFile.Close()
        }
        

        【讨论】:

        • 这种方法对我来说非常适用于一个 6GB 的文件,我需要在紧急情况下将其拆分,以便更有效地以更小的块进行分析。感谢发帖!
        • 我经历了几次运行才弄清楚这个脚本是如何工作的。我做了一个要点,以防有人感兴趣:gist.github.com/awayken/5861923
        • 你有什么理由不使用StreamReader?这样你就可以用新行拆分?
        • @stej 基于此答案,我根据需要在答案中添加了流式阅读器版本。
        • 如果您将这些行添加到脚本的请求中以定义变量并修改它们以适合您尝试拆分的文件,那么您将一切就绪! $from = "C:\temp\large_log.txt" $rootName = "C:\temp\large_log_chunk" $ext = "txt"
        【解决方案6】:

        还有这种快速(而且有点脏)的单线:

        $linecount=0; $i=0; Get-Content .\BIG_LOG_FILE.txt | %{ Add-Content OUT$i.log "$_"; $linecount++; if ($linecount -eq 3000) {$I++; $linecount=0 } }
        

        您可以通过更改硬编码的 3000 值来调整每批的第一行数。

        【讨论】:

        • 这基本上是上面答案的精确副本
        【解决方案7】:

        这样做:

        文件 1

        还有这种快速(而且有点脏)的单线:

            $linecount=0; $i=0; 
            Get-Content .\BIG_LOG_FILE.txt | %
            { 
              Add-Content OUT$i.log "$_"; 
              $linecount++; 
              if ($linecount -eq 3000) {$I++; $linecount=0 } 
            }
        

        您可以通过更改硬编码的 3000 值来调整每批的第一行数。

        Get-Content C:\TEMP\DATA\split\splitme.txt | Select -First 5000 | out-File C:\temp\file1.txt -Encoding ASCII
        

        文件 2

        Get-Content C:\TEMP\DATA\split\splitme.txt | Select -Skip 5000 | Select -First 5000 | out-File C:\temp\file2.txt -Encoding ASCII
        

        文件 3

        Get-Content C:\TEMP\DATA\split\splitme.txt | Select -Skip 10000 | Select -First 5000 | out-File C:\temp\file3.txt -Encoding ASCII
        

        等等……

        【讨论】:

        • 谢谢我最终使用了这个......但不要忘记为 outfile 添加 -width 否则它可能会将你的输出截断为 80 个字符......这也一次运行一行...... . 使用 gc -readcount 1000 更快 | select -first 5 ... 一次执行 1000 行 ... 最后 gc 将读取整个文件,而 select 将忽略其中的大部分 ... 将 -totalcount 参数包含在 gc 中会在一定数量后停止行数...也可以 -tail 结束文件
        【解决方案8】:

        根据行数(在本例中为 100)进行拆分的简单单行:

        $i=0; Get-Content .....log -ReadCount 100 | %{$i++; $_ | Out-File out_$i.txt}
        

        【讨论】:

        • 很好,值得注意的是它的默认值似乎是 UTF16LE。如果您不需要它,请添加编码类型Out-File out_$i.txt -Encoding UTF8}
        • 什么是.....log?目标文件名?
        • 婧,.....log是源文件名。
        【解决方案9】:

        其中许多答案对于我的源文件来说太慢了。我的源文件是 10 MB 到 800 MB 之间的 SQL 文件,需要拆分成行数大致相等的文件。

        我发现以前使用 Add-Content 的一些答案非常慢。等待数小时才能完成拆分并不少见。

        我没有尝试Typhlosaurus's answer,但它看起来只按文件大小而不是行数进行拆分。

        以下内容符合我的目的。

        $sw = new-object System.Diagnostics.Stopwatch
        $sw.Start()
        Write-Host "Reading source file..."
        $lines = [System.IO.File]::ReadAllLines("C:\Temp\SplitTest\source.sql")
        $totalLines = $lines.Length
        
        Write-Host "Total Lines :" $totalLines
        
        $skip = 0
        $count = 100000; # Number of lines per file
        
        # File counter, with sort friendly name
        $fileNumber = 1
        $fileNumberString = $filenumber.ToString("000")
        
        while ($skip -le $totalLines) {
            $upper = $skip + $count - 1
            if ($upper -gt ($lines.Length - 1)) {
                $upper = $lines.Length - 1
            }
        
            # Write the lines
            [System.IO.File]::WriteAllLines("C:\Temp\SplitTest\result$fileNumberString.txt",$lines[($skip..$upper)])
        
            # Increment counters
            $skip += $count
            $fileNumber++
            $fileNumberString = $filenumber.ToString("000")
        }
        
        $sw.Stop()
        
        Write-Host "Split complete in " $sw.Elapsed.TotalSeconds "seconds"
        

        对于一个 54 MB 的文件,我得到了输出...

        Reading source file...
        Total Lines : 910030
        Split complete in  1.7056578 seconds
        

        我希望其他寻找符合我要求的简单、基于行的拆分脚本的人会发现这很有用。

        【讨论】:

        • 但这会消耗大量内存。我正在尝试使用 streamreader/writer 重写
        • 在下面查看我的答案以获得内存友好、基于新行的拆分
        • 如果它发生在几秒钟内,那么我看不出为什么内存是一个问题。我等了 10 分钟,“答案”解决方案最终什么也没做,而我实施了这个解决方案,它在 5 秒多一点内完成。
        • 确实很快,我必须拆分一个 740Mb 的文件,运行需要 19s。对于同一个文件,可接受的解决方案运行了 73 (!) 分钟。这绝对是我的选择。
        【解决方案10】:

        与此处的所有答案相同,但使用 StreamReader/StreamWriter 在新行上进行拆分(逐行,而不是尝试一次将整个文件读入内存)。这种方法可以以我所知道的最快方式拆分大文件。

        注意:我做的错误检查很少,所以我不能保证它会在你的情况下顺利运行。它对我来说是这样(1.7 GB TXT 文件,400 万行,每个文件在 95 秒内分成 100,000 行)。

        #split test
        $sw = new-object System.Diagnostics.Stopwatch
        $sw.Start()
        $filename = "C:\Users\Vincent\Desktop\test.txt"
        $rootName = "C:\Users\Vincent\Desktop\result"
        $ext = ".txt"
        
        $linesperFile = 100000#100k
        $filecount = 1
        $reader = $null
        try{
            $reader = [io.file]::OpenText($filename)
            try{
                "Creating file number $filecount"
                $writer = [io.file]::CreateText("{0}{1}.{2}" -f ($rootName,$filecount.ToString("000"),$ext))
                $filecount++
                $linecount = 0
        
                while($reader.EndOfStream -ne $true) {
                    "Reading $linesperFile"
                    while( ($linecount -lt $linesperFile) -and ($reader.EndOfStream -ne $true)){
                        $writer.WriteLine($reader.ReadLine());
                        $linecount++
                    }
        
                    if($reader.EndOfStream -ne $true) {
                        "Closing file"
                        $writer.Dispose();
        
                        "Creating file number $filecount"
                        $writer = [io.file]::CreateText("{0}{1}.{2}" -f ($rootName,$filecount.ToString("000"),$ext))
                        $filecount++
                        $linecount = 0
                    }
                }
            } finally {
                $writer.Dispose();
            }
        } finally {
            $reader.Dispose();
        }
        $sw.Stop()
        
        Write-Host "Split complete in " $sw.Elapsed.TotalSeconds "seconds"
        

        输出拆分 1.7 GB 文件:

        ...
        Creating file number 45
        Reading 100000
        Closing file
        Creating file number 46
        Reading 100000
        Closing file
        Creating file number 47
        Reading 100000
        Closing file
        Creating file number 48
        Reading 100000
        Split complete in  95.6308289 seconds
        

        【讨论】:

        • 对于想要使用上述解决方案并且还具有重复标题的人,您需要做的一个步骤是在注释之后添加代码 - $writer.WriteLine($header) - "读取 $linesperFile"。 $header 将是您需要在代码的初始部分中使用所有所需列声明的变量。感谢@Vincent 提供超快的解决方案
        • 使用 Measure-Object 可能比秒表更好,但这很好。
        • 我花了 37 分钟来拆分一个 10gb 的文件。得到的解决方案在我取消它之前运行了 30 分钟,并且还没有成功地将文件放入内存,可能是因为我没有 10gb 的可用内存。
        • 对于@VKarthik的header解决方案,你也可以从文件的第一行自动初始化header,方法是把$header = $reader.ReadLine();放在$reader = [io.file]::OpenText($filename)之后
        【解决方案11】:

        我的要求有点不同。我经常使用逗号分隔和制表符分隔的 ASCII 文件,其中单行是单条数据记录。而且它们真的很大,所以我需要将它们分成可管理的部分(同时保留标题行)。

        所以,我恢复了我的经典 VBScript 方法,并拼凑了一个可以在任何 Windows 计算机上运行的小型 .vbs 脚本(它由 Window 上的 WScript.exe 脚本主机引擎自动执行)。

        这种方法的好处是它使用文本流,因此不会将底层数据加载到内存中(或者至少不是一次全部加载)。结果是它非常快,并且运行起来并不需要太多内存。我刚刚在 i7 上使用此脚本拆分的测试文件的文件大小约为 1 GB,大约有 1200 万行文本,并被拆分为 25 个部分文件(每个部分文件大约有 50 万行)——处理大约需要 2 分钟它在任何时候都没有超过 3 MB 的内存使用。

        这里需要注意的是,它依赖于具有“行”的文本文件(意味着每条记录都用 CRLF 分隔),因为文本流对象使用“ReadLine”函数一次处理一行。但是,嘿,如果您使用的是 TSV 或 CSV 文件,那就完美了。

        Option Explicit
        
        Private Const INPUT_TEXT_FILE = "c:\bigtextfile.txt"  
        Private Const REPEAT_HEADER_ROW = True                
        Private Const LINES_PER_PART = 500000                 
        
        Dim oFileSystem, oInputFile, oOutputFile, iOutputFile, iLineCounter, sHeaderLine, sLine, sFileExt, sStart
        
        sStart = Now()
        
        sFileExt = Right(INPUT_TEXT_FILE,Len(INPUT_TEXT_FILE)-InstrRev(INPUT_TEXT_FILE,".")+1)
        iLineCounter = 0
        iOutputFile = 1
        
        Set oFileSystem = CreateObject("Scripting.FileSystemObject")
        Set oInputFile = oFileSystem.OpenTextFile(INPUT_TEXT_FILE, 1, False)
        Set oOutputFile = oFileSystem.OpenTextFile(Replace(INPUT_TEXT_FILE, sFileExt, "_" & iOutputFile & sFileExt), 2, True)
        
        If REPEAT_HEADER_ROW Then
            iLineCounter = 1
            sHeaderLine = oInputFile.ReadLine()
            Call oOutputFile.WriteLine(sHeaderLine)
        End If
        
        Do While Not oInputFile.AtEndOfStream
            sLine = oInputFile.ReadLine()
            Call oOutputFile.WriteLine(sLine)
            iLineCounter = iLineCounter + 1
            If iLineCounter Mod LINES_PER_PART = 0 Then
                iOutputFile = iOutputFile + 1
                Call oOutputFile.Close()
                Set oOutputFile = oFileSystem.OpenTextFile(Replace(INPUT_TEXT_FILE, sFileExt, "_" & iOutputFile & sFileExt), 2, True)
                If REPEAT_HEADER_ROW Then
                    Call oOutputFile.WriteLine(sHeaderLine)
                End If
            End If
        Loop
        
        Call oInputFile.Close()
        Call oOutputFile.Close()
        Set oFileSystem = Nothing
        
        Call MsgBox("Done" & vbCrLf & "Lines Processed:" & iLineCounter & vbCrLf & "Part Files: " & iOutputFile & vbCrLf & "Start Time: " & sStart & vbCrLf & "Finish Time: " & Now())
        

        【讨论】:

          【解决方案12】:

          听起来像是 UNIX 命令拆分的工作:

          split MyBigFile.csv
          

          只需在 10 分钟内将我的 55 GB csv 文件拆分为 21k 块。

          虽然它不是 PowerShell 原生的,但它附带了例如 git for windows 包 https://git-scm.com/download/win

          【讨论】:

            【解决方案13】:

            由于日志中的行可以是可变的,我认为最好在每个文件方法中采用多行。以下代码 sn-p 在 19 秒(18.83.. 秒)内处理了 400 万行日志文件,将其拆分为 500,000 行块:

            $sourceFile = "c:\myfolder\mylargeTextyFile.csv"
            $partNumber = 1
            $batchSize = 500000
            $pathAndFilename = "c:\myfolder\mylargeTextyFile part $partNumber file.csv"
            
            [System.Text.Encoding]$enc = [System.Text.Encoding]::GetEncoding(65001)  # utf8 this one
            
            $fs=New-Object System.IO.FileStream ($sourceFile,"OpenOrCreate", "Read", "ReadWrite",8,"None") 
            $streamIn=New-Object System.IO.StreamReader($fs, $enc)
            $streamout = new-object System.IO.StreamWriter $pathAndFilename
            
            $line = $streamIn.readline()
            $counter = 0
            while ($line -ne $null)
            {
                $streamout.writeline($line)
                $counter +=1
                if ($counter -eq $batchsize)
                {
                    $partNumber+=1
                    $counter =0
                    $streamOut.close()
                    $pathAndFilename = "c:\myfolder\mylargeTextyFile part $partNumber file.csv"
                    $streamout = new-object System.IO.StreamWriter $pathAndFilename
            
                }
                $line = $streamIn.readline()
            }
            $streamin.close()
            $streamout.close()
            

            这可以很容易地转换为带有参数的函数或脚本文件,以使其更加通用。它使用StreamReaderStreamWriter 来实现其速度和极小的内存占用

            【讨论】:

              【解决方案14】:

              这是我将名为 patch6.txt 的文件(大约 32,000 行)拆分为每个 1000 行的单独文件的解决方案。它并不快,但它可以完成工作。

              $infile = "D:\Malcolm\Test\patch6.txt"
              $path = "D:\Malcolm\Test\"
              $lineCount = 1
              $fileCount = 1
              
              foreach ($computername in get-content $infile)
              {
                  write $computername | out-file -Append $path_$fileCount".txt"
                  $lineCount++
              
                  if ($lineCount -eq 1000)
                  {
                      $fileCount++
                      $lineCount = 1
                  }
              }
              

              【讨论】:

              • 为什么要追加?在 "$fileCount" 之后的同一行还有一个额外的双引号,这不起作用