PowerShell处理超大单行TXT文件遇System.OutOfMemoryException求助
处理超大单行文本文件的内存友好分割方案
我之前也踩过这个坑——Get-Content在面对这种超大单行文件时完全水土不服,它默认会把整行(哪怕是几十GB的单行)一股脑塞进内存,哪怕你总内存够,.NET对字符串的Unicode存储机制也会让内存占用直接翻倍(比如1GB的ASCII文本加载后会变成2GB,再加上PowerShell的额外开销,很容易触发System.OutOfMemoryException)。
核心解决思路就是绝对不要把整个文件加载到内存,改用流式逐块处理,下面给你两个实用的PowerShell方案:
方案1:按字节分割(高效,适合不担心字符拆分的场景)
这个方法直接操作字节流,内存占用只和缓冲区大小有关(全程几十MB顶天),速度最快,适合纯ASCII或不介意多字节字符被拆分的文件:
# 先配置参数,根据你的需求调整 $inputFilePath = "C:\path\to\your\huge_single_line.txt" $outputFilePrefix = "split_part_" $maxFileSizeBytes = 100MB # 每个分割文件的最大大小,比如1GB就写1GB $bufferSize = 1MB # 每次读取的缓冲区大小,不用太大 # 初始化流和计数器 $fileCounter = 1 $currentOutputPath = "$outputFilePrefix$fileCounter.txt" $inputStream = [System.IO.File]::OpenRead($inputFilePath) $outputStream = [System.IO.File]::Create($currentOutputPath) $totalBytesWritten = 0 $bytesRead = 0 try { $buffer = New-Object byte[] $bufferSize # 循环逐块读取文件 while (($bytesRead = $inputStream.Read($buffer, 0, $bufferSize)) -gt 0) { # 检查当前文件是否即将超过设定大小 if ($totalBytesWritten + $bytesRead -gt $maxFileSizeBytes) { # 先把当前文件填满 $remainingBytes = $maxFileSizeBytes - $totalBytesWritten $outputStream.Write($buffer, 0, $remainingBytes) $outputStream.Flush() $outputStream.Close() # 切换到新的输出文件 $fileCounter++ $currentOutputPath = "$outputFilePrefix$fileCounter.txt" $outputStream = [System.IO.File]::Create($currentOutputPath) # 把剩余的字节写入新文件 $outputStream.Write($buffer, $remainingBytes, $bytesRead - $remainingBytes) $totalBytesWritten = $bytesRead - $remainingBytes } else { # 直接写入当前文件 $outputStream.Write($buffer, 0, $bytesRead) $totalBytesWritten += $bytesRead } } } finally { # 确保所有流都关闭,避免资源泄漏 $inputStream.Close() $outputStream.Close() Write-Host "分割完成!共生成$fileCounter个文件。" }
方案2:按字符分割(安全,适合多字节编码文件)
如果你的文件是UTF-8等多字节编码,担心字节分割会把一个字符拆成两半,可以用这个按字符处理的版本,内存占用依然很低:
# 配置参数 $inputFilePath = "C:\path\to\your\huge_single_line.txt" $outputFilePrefix = "split_part_" $maxCharCount = 100000000 # 每个文件最多存储的字符数,按需调整 $encoding = [System.Text.Encoding]::UTF8 # 匹配你的文件编码 # 初始化读写器和计数器 $fileCounter = 1 $currentOutputPath = "$outputFilePrefix$fileCounter.txt" $reader = [System.IO.StreamReader]::new($inputFilePath, $encoding) $writer = [System.IO.StreamWriter]::new($currentOutputPath, $false, $encoding) $charBuffer = New-Object char[] 1024 $currentCharCount = 0 try { # 循环逐块读取字符 while (($charsRead = $reader.Read($charBuffer, 0, $charBuffer.Length)) -gt 0) { if ($currentCharCount + $charsRead -gt $maxCharCount) { # 填满当前文件的剩余字符配额 $remainingChars = $maxCharCount - $currentCharCount $writer.Write($charBuffer, 0, $remainingChars) $writer.Flush() $writer.Close() # 切换到新文件 $fileCounter++ $currentOutputPath = "$outputFilePrefix$fileCounter.txt" $writer = [System.IO.StreamWriter]::new($currentOutputPath, $false, $encoding) # 写入剩余字符到新文件 $writer.Write($charBuffer, $remainingChars, $charsRead - $remainingChars) $currentCharCount = $charsRead - $remainingChars } else { # 直接写入当前文件 $writer.Write($charBuffer, 0, $charsRead) $currentCharCount += $charsRead } } } finally { $reader.Close() $writer.Close() Write-Host "分割完成!共生成$fileCounter个文件。" }
这两个方案都是流式处理,只会把当前处理的小块数据留在内存里,不管你的文件是1GB还是10GB,都不会再触发内存溢出问题。
内容的提问来源于stack exchange,提问作者FatalBulletHit
相关产品推荐
相关产品推荐

