Powershell如何从70GB超大CSV日志中完整提取多行JSON数据
超大CSV内嵌JSON提取无遗漏方案
原方案的核心问题是固定分块边界容易切断跨多行的JSON对象,导致匹配遗漏。解决核心是引入跨块残留缓存机制,配合流式IO实现低内存、无遗漏提取,同时可大幅提升大文件处理性能。
优化实现代码
# 路径配置 $inputPath = "<替换为源CSV文件路径>" $outputPath = "<替换为JSON输出路径>" $blockSize = 20MB # 分块大小可根据内存调整,建议设为10-50MB,比按行分块稳定性更高 # 初始化流式读写对象,注意编码要和源文件一致,源文件为GBK则替换为[System.Text.Encoding]::GetEncoding("GBK") $reader = New-Object System.IO.StreamReader($inputPath, [System.Text.Encoding]::UTF8) $writer = New-Object System.IO.StreamWriter($outputPath, $false, [System.Text.Encoding]::UTF8) # 预编译带括号平衡的JSON匹配正则,支持匹配任意层级嵌套的合法JSON,比简单非贪婪匹配准确率更高 $jsonRegex = [regex]::new('(?s)\{(?>[^{}]+|\{(?<c>)|\}(?<-c>))*(?(c)(?!))\}', [System.Text.RegularExpressions.RegexOptions]::Compiled) # 残留缓存:存储上一个分块末尾未匹配完成的内容 $residue = "" $buffer = New-Object char[] $blockSize $readLength = 0 while (($readLength = $reader.Read($buffer, 0, $blockSize)) -gt 0) { # 拼接上一轮残留 + 当前分块内容再匹配 $currentContent = $residue + [string]::new($buffer, 0, $readLength) $matches = $jsonRegex.Matches($currentContent) if ($matches.Count -gt 0) { # 输出所有完整匹配的JSON foreach ($m in $matches) { $writer.WriteLine($m.Value) } # 截取最后一个匹配结束后的内容作为新的残留,留到下一轮拼接 $lastMatchEndPos = $matches[$matches.Count - 1].Index + $matches[$matches.Count - 1].Length $residue = $currentContent.Substring($lastMatchEndPos) } else { # 没有匹配到完整JSON,所有内容全部留到下一轮 $residue = $currentContent } } # 处理最后残留的内容,避免遗漏文件末尾的JSON $finalMatches = $jsonRegex.Matches($residue) foreach ($m in $finalMatches) { $writer.WriteLine($m.Value) } # 释放IO资源 $reader.Close() $writer.Close()
方案特点
- 完全无遗漏:跨块缓存机制保证所有被分块切断的JSON都会被完整拼接后再匹配,不会出现截断遗漏
- 内存占用稳定:固定分块大小,70GB文件仅需几十MB内存即可运行
- 处理性能更高:流式IO+预编译正则的组合,处理速度是原Get-Content+Add-Content方案的3~5倍
- 适配性强:支持任意行数、任意嵌套层级的JSON提取,不需要提前知道JSON结构
内容的提问来源于stack exchange,提问作者MatsonWatson
相关产品推荐
相关产品推荐

