【问题标题】:How to split the string more efficiently?如何更有效地拆分字符串?
【发布时间】:2016-09-16 07:01:23
【问题描述】:

我有一个 JSON 字符串,如下所示:

{"Detail": [
  {"PrimaryKey":111,"Date":"2016-09-01","Version":"7","Count":2,"Name":"Windows","LastAccessTime":"2016-05-25T21:49:52.36Z"},
  {"PrimaryKey":222,"Date":"2016-09-02","Version":"8","Count":2,"Name":"Windows","LastAccessTime":"2016-07-25T21:49:52.36Z"},
  {"PrimaryKey":333,"Date":"2016-09-03","Version":"9","Count":3,"Name":"iOS","LastAccessTime":"2016-08-22T21:49:52.36Z"},
  .....( *many values )
]} 

数组Detail 有很多PrimaryKeys。有时,大约是 500K PrimaryKeys。我们使用的系统只能处理一定长度的 JSON 字符串,即 128KB。所以我必须将这个 JSON 字符串分割成段(每个段的长度为 128KB 或更少的字符)。

Regex reg = new Regex(@"\{"".{0," + (128*1024).ToString() + @"}""\}");
MatchCollection mc = reg.Matches(myListString);

目前,我使用正则表达式来执行此操作。它工作正常。但是,它使用了太多的内存。有没有更好的方法来做到这一点(不必是正则表达式)?

*** 添加了更多信息。

我上面提到的“系统”是 Azure DocumentDB。默认情况下,文档只能是 512KB(和现在一样)。虽然我们可以要求 MS 增加这个,但是我们得到的 json 文件总是远远超过 512KB。这就是为什么我们需要想办法做到这一点。

如果可能,我们希望继续使用 documentDB,但我们愿意接受其他建议。

*** 一些信息让事情变得清晰:1)数组中的值不同。不重复。 2) 是的,我尽可能使用 StringBuilder。 3) 是的,我试过IndexOf & Substring,但是根据测试,这种情况下性能并不比正则表达式好(虽然可能是我实现的方式)。

* **json 对象很复杂,但我只关心这个“详细信息”,它是一个数组。我们可以假设字符串就像示例一样,只有“详细信息”。我们需要将这个 json 数组字符串拆分成小于 512KB 的大小。基本上,我们可以认为这是一个简单的字符串,而不是 json。但是,它是 json 格式,所以也许有些库可以做得更好。

【问题讨论】:

  • 什么系统? (我们使用的系统只能处理一定长度的json字符串,即128KB)
  • 您基本上必须将您的 json 重写为长度更短的有效 json,或者您所说的“系统”是自制软件并且并不关心它是否是有效的 json?为什么不看看 json.net 或没有这种限制的东西?
  • 如果您知道数据的确切格式并且不关心在格式更改时破坏解析代码,我认为IndexOfSubstring 可以解决问题。
  • 嗯,你有 3 行 duplicated 数据。如果您将 one 行改为 Key "PrimaryKey":null,"Date":"2016-09-06","Version":"7","Count":2,"Name":"Windows",并将日期数组或集合作为 Value
  • @Fabjan,你是认真的吗?很明显,JSON 对象在生产中会有所不同。 OP 显然只是复制并粘贴同一行以作为示例。

标签: c# .net json azure azure-cosmosdb


【解决方案1】:

查看Json.NET(可通过 NuGet 获得)。

它有一个JsonReader 类,它允许您通过令牌读取json 来创建所需的对象,example of json reading with JsonReader。并不是说如果您将无效的 json 字符串(例如,没有“结束数组”字符或没有“结束对象”字符)传递给 JsonReader - 它只会在到达无效项目时抛出异常,因此您可以将不同的子字符串传递给它。

另外,我猜你的系统有类似JsonReader的东西,所以你可以使用它。

StringReader 读取string 应该不需要太多的应用程序内存,并且应该比通过正则表达式匹配迭代更快。

【讨论】:

  • JsonReader 在这种情况下不会更好地工作。因为数组的json字符串很长,现在我们要把它们拆分成多个字符串,每个小于512KB。这意味着我们需要遍历对象,找出元素的长度,添加它们直到字符串达到 512KB,输出字符串,然后处理下一个对象。这显然比直接通过 IndexOf 处理字符串要慢,因为我们不需要解析对象。在这种情况下,根据测试,IndexOf 比正则表达式慢。谢谢。
【解决方案2】:

这是一个 hacky 解决方案,假设 data 包含您的 JSON 数据:

var details = data
    .Split('[')[1]
    .Split(']')[0]
    .Split(new[] { "}," }, StringSplitOptions.None)
    .Select(d => d.Trim())
    .Select(d => d.EndsWith("}") ? d : d + "}");;

foreach (var detail in details)
{
    // Now process "detail" with your JSON library.
}

工作示例:https://dotnetfiddle.net/sBQjyi

显然,只有在您确实无法使用普通 JSON 库时才应该这样做。有关库建议,请参阅 Mikhail Neofitov's answer

如果您正在从文件或网络读取 JSON 数据,您应该实现更类似于流的处理,在该处理中您读取一个详细信息行,使用您的 JSON 库对其进行反序列化并将其生成给调用者。当调用者请求下一个细节对象时,读取下一行,反序列化它等等。这样您就可以最大限度地减少反序列化器的内存占用。

【讨论】:

    【解决方案3】:

    您可能需要考虑将每个详细信息存储在单独的文档中。这意味着要获得标头和所有详细文档需要两次往返,但这意味着您永远不会处理非常大的 JSON 文档。此外,如果以增量方式添加 Detail,则写入效率会更高,因为无法仅添加另一行。您必须重写整个文档。您的读/写比率将决定整体效率的收支平衡点。

    对此的另一个论点是正则表达式解析的复杂性,通过您的 JSON 解析器提供它,然后重新组装它就消失了。您永远不知道您的正则表达式解析器是否会处理所有情况(引号内的逗号、国际字符等)。我见过很多人认为他们有一个很好的正则表达式只是为了在生产中找到奇怪的情况。

    如果您的 Detail 数组可以无限增长(甚至有很大的界限),那么无论您的 JSON 解析器限制或读/写比率如何,您都绝对应该进行此更改,因为最终您会超过限制。

    【讨论】:

    • 感谢 larry!你是 100% 正确的。正则表达式将无法处理所有可能性。但这还不是我关心的问题。至于将细节保存为文档,实际上,这是我需要解决的“下一个”问题。随着时间的推移,我们希望向 Detail 数组添加更多内容,但由于我们拥有如此多的数据,最终,它可能拥有比一个集合所允许的更多的文档。所以我们决定暂时假设没有添加细节。现在,如果我们将 Detail 中的每个元素保存为一个文档,它会出现性能问题,因为当我们需要使用 Detail 时,我们需要获取数十万个文档。
    • 我强烈建议现在就使用分区集合,因此您不必担心它会超出单个集合。我不会假设当它们都在自己的文档中时收集详细信息会更慢。当然,标题需要往返一次,细节需要往返一次,但整体阅读可能没有您想象的那么大。此外,这种方式的写入速度会快得多,特别是如果您稍后添加详细信息。
    猜你喜欢
    • 1970-01-01
    • 2020-01-09
    • 2023-03-08
    • 2020-01-27
    • 1970-01-01
    • 1970-01-01
    • 2011-02-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多