为什么这个问题不重复? (看到评论后添加)
我有200多个文件,每个文件都是2 GB以上,意味着总大小为400多GB。这些文件中的每一行都是一个json字符串。我事先没有json schema
的记录。我的工作是找到这些文件中的所有密钥。
我编写了以下代码来获取所有这些json记录中的所有不同键。我使用来自for-loop
的多线程main
来调用以下方法。
private void GetTokensFromJson(string filePath)
{
IEnumerable<string> txts = File.ReadLines(filePath, Encoding.UTF8);
Console.WriteLine(txts.Count());
List<string> distinctKeys = new List<string>();
foreach (var text in txts)
{
string pattern = "{\"";
foreach (Match m in Regex.Matches(text, pattern))
{
//string matchValue = m.Value;
int matchIndex = m.Index;
string subStr=text.Substring(matchIndex+2, text.Length - matchIndex - 3);
int quoteIndex=subStr.IndexOf('\"');
string jsonKey = subStr.Substring(0, quoteIndex);
if (!distinctKeys.Contains(jsonKey) && !jsonKey.Contains("\\"))
{
Console.WriteLine(jsonKey);
distinctKeys.Add(jsonKey);
}
}
string secondPattern="\":";
foreach (Match m in Regex.Matches(text, secondPattern))
{
int matchIndex = m.Index;
string revJsonKKey = "";
while(matchIndex>0)
{
matchIndex--;
if (text[matchIndex] != '\"')
revJsonKKey += text[matchIndex];
else
break;
}
IEnumerable<char> jsonKeyCharArray = revJsonKKey.Reverse();
string jsonKey="";
foreach(char c in jsonKeyCharArray)
{
jsonKey += c;
}
if (!distinctKeys.Contains(jsonKey) && !jsonKey.Contains("\\"))
{
Console.WriteLine(jsonKey);
distinctKeys.Add(jsonKey);
}
}
}
distinctKeys
拥有所有不同的json键。但我错过了几个键并添加了不需要的键,不知道为什么:|。我无法调试给定的输入,因为它太大了!此外,这种方法太慢了。
为了让事情更清楚,让我们举一个例子,如果文件有json,
{"id":"123", "name":"hello, world", "department":[{"name":"dept1", "deptID":"123"}]}
{"id":"456324", "department":[{"name":"dept2", "deptID":"456"}]}
预期输出为id,name,department, department->name, department->deptID
。输出格式无关紧要。请注意,并非所有json记录都没有所有密钥,json记录可以包含嵌套的json记录。
我两个问题,
答案 0 :(得分:3)
尝试使用Json.net,Path属性包含该对象的完整路径
private static void GetKeys(JObject obj, List<string> keys)
{
var result = obj.Descendants()
.Where(f => f is JProperty) //.Where(f => f is JProperty)
.Select(f => f as JProperty)// and .Select(f => f as JProperty) can be replaced with .OfType<JProperty>()
.Select(f=>f.Path)
.Where(f=> !keys.Contains(f));
keys.AddRange(result);
}
static void Main(string[] args)
{
IEnumerable<string> txts = @"{'id':'123', 'name':'hello, world', 'department':[{'name':'dept1', 'deptID':'123'}]}
{'id':'456324', 'department':[{'name':'dept2', 'deptID':'456'}]}".Split("\r\n".ToArray(),StringSplitOptions.RemoveEmptyEntries);
List<string> keys = new List<string>();
foreach (var item in txts)
{
var obj = JObject.Parse(item);
GetKeys(obj, keys);
}
}
答案 1 :(得分:1)
将字符串读入JSON.NET并将其转换为Jobjects
然后循环访问Jobjects
foreach ( jobject in jobjects )
{
IList<string> keys = jobject .Properties().Select(p => p.Name).ToList();
}
然后做
keys.distinct();
就像
private void GetTokensFromJson(string filePath)
{
IEnumerable<string> txts = File.ReadLines(filePath, Encoding.UTF8);
List<JObject> jObjects = new List<JObject>() {};
IList<string> keyslist;
Console.WriteLine(txts.Count());
List<string> distinctKeys = new List<string>();
foreach (var text in txts)
{
var obj = JObject.Parse(text);
jObjects.add(obj);
}
for each ( jobject in jobjects )
{
IList<string> keys = jobject .Properties().Select(p => p.Name).ToList();
keyslist.add(keys);
}
keyslist.distinct();
}
答案 2 :(得分:1)
让我们做数学吧。你有:
这使得400 GB内存仅用于保存所有内容和 1,789,569,707,即近2 十亿行。
显然,你的问题是不是与解析相关的问题,而是以增量方式管理内存和索引,使用并非全部驻留在内存中的中间结果。
使用您现在拥有的简单列表来跟踪您的密钥,并假设您的密钥中 1/20 是唯一的:
搜索列表(1.25亿个项目)以获取新行的重复项将非常缓慢。
您可能需要查看map / reduce风格算法,以确定如何实现这样的效果。
答案 3 :(得分:1)
一些问题:
不要Console.WriteLine(txts.Count());
。我相信这实际上会让你读两遍整个文件 - 一次计数,一次读取密钥。
使用HashSet<string>
收集不同的密钥,它比使用列表快得多。
正如Kenner Dev建议的那样,安装Json.NET并使用LINQ to JSON来解析文件的每一行,而无需了解架构。
继续按照您目前的操作逐行阅读文件,不要尝试以任何方式一次性将整个内容加载到内存中。
然后,GetTokensFromJson
变为:
public static HashSet<string> GetTokensFromJson(IEnumerable<string> txts)
{
return new HashSet<string>(txts.Select(t => JObject.Parse(t)).Where(o => o != null).SelectMany(o => o.Descendants().OfType<JProperty>()).Select(p => p.Name));
}