在C#strings对象之间共享字符缓冲区

在C#strings对象之间共享字符缓冲区,c#,parsing,memory-management,substring,C#,Parsing,Memory Management,Substring,这可能吗?考虑到C#使用不可变字符串,我们可以预期会有一种方法: var expensive = ReadHugeStringFromAFile(); var cheap = expensive.SharedSubstring(1); 如果没有这样的函数,为什么还要让字符串不可变呢? 或者,如果字符串由于其他原因已经是不可变的,为什么不提供这个方法呢 我研究这个的具体原因是做一些文件解析。简单的递归下降解析器(比如TinyPG生成的解析器,或者手工编写的解析器)到处都使用子字符串。这意味着,如

这可能吗?考虑到C#使用不可变字符串,我们可以预期会有一种方法:

var expensive = ReadHugeStringFromAFile();
var cheap = expensive.SharedSubstring(1);
如果没有这样的函数,为什么还要让字符串不可变呢? 或者,如果字符串由于其他原因已经是不可变的,为什么不提供这个方法呢

我研究这个的具体原因是做一些文件解析。简单的递归下降解析器(比如TinyPG生成的解析器,或者手工编写的解析器)到处都使用子字符串。这意味着,如果你给他们一个大文件来解析,内存流失是难以置信的。当然有解决方法——基本上是滚动您自己的子字符串类,然后当然忘记了能够使用诸如StartsWith之类的字符串方法或诸如Regex之类的字符串库,所以您也需要滚动您自己的版本。我假设像ANTLR这样的解析器生成器基本上可以做到这一点,但我的格式非常简单,不足以证明使用这种怪物工具是合理的。即使是TinyPG也可能是一种过度杀伤力


有人请告诉我,我遗漏了一些明显或不太明显的标准C方法调用…

不,没有类似的

.NET字符串直接包含它们的文本数据,而Java字符串有对字符数组的引用、偏移量和长度

这两种解决方案在某些情况下都是“赢”,在另一些情况下则是“输”


如果您确信这将是一个杀手锏,那么您可以实现一个Java风格的字符串,用于您自己的内部API。

NET framework支持。这是一个局部解决方案,但不提供重用字符串部分的可能性。我认为重用子字符串会导致一些问题,乍一看并不是那么明显。如果您必须使用进行大量的字符串操作,那么这是一种方法。

据我所知,所有较大的解析器都使用流进行解析。这不适合您的情况吗?

C#中没有任何东西提供您所需要的现成功能

我们需要的是一个不可变的数据结构,它支持O(1)个concats和O(logn)子字符串。我找不到任何rope的C#实现,但是


除此之外,如果TinyPG或ANTLR是最简单的方法,那么使用它也没有什么错。

好吧,你可以自己使用“不安全”来做内存管理,这可能会让你做你想做的事情。此外,StringBuilder类对于需要多次操作字符串的情况也非常适用,因为它不会在每次操作中生成新字符串。

您可以轻松编写一个简单的类来表示“廉价”。它将只保存子字符串开头的索引和子字符串的长度。有两种方法可以让您在需要时读取子字符串-字符串转换运算符是您理想的选择

string text = myCheapObject;
它将无缝地工作,就像它是一个实际的字符串一样。添加对一些便捷方法的支持,如StartsWith,将是快速而简单的(它们都是一行程序)


另一种选择是编写一个常规解析器,并将您的令牌存储在一个字典中,您可以从中共享对令牌的引用,而不是保留多个副本。

String interning(在Java和C#中)是一种死机,因为它要求您在调用Intern之前构造一个String对象。为了达到节省内存使用的目的,您希望能够向它传递一个StringBuilder或(字符数组的一部分)之类的东西。但是,不,“那太容易了”这是真的。。。但是GC…:由于我无法在不构造字符串(从而复制字符)的情况下将正则表达式应用于StringBuilder的(前缀),因此在解析1M文件时仍然会遇到复制5G文本的问题。我可以说,我的令牌没有一个长度超过X个字符,并且从StringBuilder中重复生成前缀子字符串,这可能就行了。。。这不会很简单。不,因为我不能将正则表达式应用于流,甚至不能进行简单的非破坏性测试它是否以文本字符串开头。因此,直接解析流需要(至少)重新实现正则表达式的整个复杂性,这就是ANTLR所做的,这就是为什么我称它为“怪物”,而不是像TinyPG这样微不足道的东西。但是,TinyPG反复调用Substring来获取其余的输入。给它一个1MB的文件,该文件被拆分为100000个令牌,最终复制5GB的内存。哎呀!我很想这样做,只是你不能将正则表达式应用到StringBuffer:-(如果你创建一个CheapString类,它就不能与正则表达式一起工作是的,问题是字符串是密封的,所以你不能从中派生。当你必须加载和解析一个500 MB的文件时,你最终会得到1 GB(UTF8到wchar),一旦你开始分解它,它将达到2 GB。在我的情况下,元素出现无序,并且文件可能在缓慢的网络上,因此即使你可以重构以流式传输,性能也会变得很糟糕。@xenadu,你可以编写流式实现,并且仍然使用大的I/O缓冲区来保持性能良好。如果你有一组const令牌在文件中,则不应将其拆分为数千个小字符串,而应在一个小字典中仅对字符串进行数千个引用(或索引)。类似地,如果将数字表示为“123765.6567853567896”这样的字符串然后将其转换为浮点将节省36字节或更多。通常,您可以做很多事情来提高速度和内存效率。如果失败,请使用64位pc和256GB ram,甚至只使用SSD。我刚刚测试了一种潜在的解决方法。解决此问题的一种方法是将正则表达式与stri的中间部分相匹配ng.如果将“^.{N}”附加到正则表达式的开头,这是可能的。但是,正则表达式库似乎不够聪明,无法在一次操作中跳过N个字符。这需要O(N)个时间,因此匹配开始需要越来越长的时间