PowerShell处理700MB JSON文件:如何高效列出所有键?
处理大体积JSON文件的高效方案
你的原代码之所以运行极慢,核心问题是**ConvertFrom-Json会将整个700MB的JSON数组一次性加载到内存并解析为PowerShell对象**,这会占用远超文件大小的内存,触发磁盘分页交换,导致速度骤降。下面是针对你的需求的高效解决方案:
一、高效提取所有唯一键
使用Newtonsoft.Json的流式读取器,无需加载整个文件到内存,逐对象提取键并自动去重:
# 先安装Newtonsoft.Json模块(首次运行需执行) Install-Module -Name Newtonsoft.Json -Scope CurrentUser -Force # 导入模块 Import-Module Newtonsoft.Json $filePath = Convert-Path -LiteralPath names.json $allKeys = [System.Collections.Generic.HashSet[string]]::new() # 自动去重的集合 # 创建流式读取器 $stream = [System.IO.File]::OpenText($filePath) $reader = [Newtonsoft.Json.JsonTextReader]::new($stream) try { # 跳过JSON数组的起始符号 while ($reader.Read() -and $reader.TokenType -ne [Newtonsoft.Json.JsonToken]::StartArray) {} # 遍历数组中的每个对象 while ($reader.Read() -and $reader.TokenType -ne [Newtonsoft.Json.JsonToken]::EndArray) { if ($reader.TokenType -eq [Newtonsoft.Json.JsonToken]::StartObject) { # 读取当前对象的所有键 while ($reader.Read() -and $reader.TokenType -ne [Newtonsoft.Json.JsonToken]::EndObject) { if ($reader.TokenType -eq [Newtonsoft.Json.JsonToken]::PropertyName) { [void]$allKeys.Add($reader.Value.ToString()) } } } } } finally { # 确保资源释放 $reader.Close() $stream.Close() } # 输出排序后的所有唯一键 $allKeys | Sort-Object
二、基于提取的键处理文件
1. 精简JSON(保留指定键)
假设你已确定需要保留的键列表$keepKeys,用流式方法过滤并输出新文件:
$keepKeys = @("word", "pos", "lang") # 替换为你要保留的键 $inputPath = Convert-Path -LiteralPath names.json $outputPath = Convert-Path -LiteralPath trimmed_names.json $streamIn = [System.IO.File]::OpenText($inputPath) $reader = [Newtonsoft.Json.JsonTextReader]::new($streamIn) $streamOut = [System.IO.File]::CreateText($outputPath) $writer = [Newtonsoft.Json.JsonTextWriter]::new($streamOut) $writer.Formatting = [Newtonsoft.Json.Formatting]::None # 关闭格式化进一步缩小体积 try { $writer.WriteStartArray() # 写入数组起始符号 $firstObject = $true while ($reader.Read() -and $reader.TokenType -ne [Newtonsoft.Json.JsonToken]::EndArray) { if ($reader.TokenType -eq [Newtonsoft.Json.JsonToken]::StartObject) { if (-not $firstObject) { $writer.WriteRaw(',') } $firstObject = $false $writer.WriteStartObject() # 读取对象键值对,仅保留指定键 while ($reader.Read() -and $reader.TokenType -ne [Newtonsoft.Json.JsonToken]::EndObject) { if ($reader.TokenType -eq [Newtonsoft.Json.JsonToken]::PropertyName) { $key = $reader.Value.ToString() if ($keepKeys -contains $key) { $writer.WritePropertyName($key) $reader.Read() # 定位到值的位置 $writer.WriteToken($reader) # 写入对应值 } else { $reader.Skip() # 跳过不需要的键值对 } } } $writer.WriteEndObject() } } $writer.WriteEndArray() # 写入数组结束符号 } finally { $reader.Close() $streamIn.Close() $writer.Close() $streamOut.Close() }
2. 转换为包含所有键的CSV
利用提取到的所有键作为表头,流式读取每个对象的对应值,避免PowerShell默认ConvertTo-Csv丢失键的问题:
$allKeys = $allKeys | Sort-Object # 固定键顺序 $inputPath = Convert-Path -LiteralPath names.json $outputPath = Convert-Path -LiteralPath output.csv # 创建CSV写入器 $csvWriter = [System.IO.StreamWriter]::new($outputPath) $csvWriter.WriteLine($allKeys -join ',') # 写入表头 $streamIn = [System.IO.File]::OpenText($inputPath) $reader = [Newtonsoft.Json.JsonTextReader]::new($streamIn) try { # 跳过数组起始符号 while ($reader.Read() -and $reader.TokenType -ne [Newtonsoft.Json.JsonToken]::StartArray) {} while ($reader.Read() -and $reader.TokenType -ne [Newtonsoft.Json.JsonToken]::EndArray) { if ($reader.TokenType -eq [Newtonsoft.Json.JsonToken]::StartObject) { $objectValues = [ordered]@{} # 读取当前对象的所有键值对 while ($reader.Read() -and $reader.TokenType -ne [Newtonsoft.Json.JsonToken]::EndObject) { if ($reader.TokenType -eq [Newtonsoft.Json.JsonToken]::PropertyName) { $key = $reader.Value.ToString() $reader.Read() # 处理CSV格式,字符串用双引号包裹并转义内部引号 $value = if ($reader.Value -is [string]) { '"' + $reader.Value.ToString().Replace('"', '""') + '"' } else { $reader.Value.ToString() } $objectValues[$key] = $value } } # 按表头顺序生成行数据,缺失的键填充空字符串 $row = $allKeys | ForEach-Object { $objectValues[$_] ?? '' } $csvWriter.WriteLine($row -join ',') } } } finally { $reader.Close() $streamIn.Close() $csvWriter.Close() }
内容的提问来源于stack exchange,提问作者Ne Mo
相关产品推荐
相关产品推荐

