从 MemoryStream 获取子字符串而不将整个流转换为字符串

Posted

技术标签:

【中文标题】从 MemoryStream 获取子字符串而不将整个流转换为字符串【英文标题】:Get substring from MemoryStream without converting entire stream to string 【发布时间】:2017-05-17 01:47:06 【问题描述】:

我希望能够有效地从 MemoryStream 中获取子字符串(最初来自 zip 中的 xml 文件)。目前,我将整个 MemoryStream 读取为一个字符串,然后搜索我想要的 xml 节点的开始和结束标记。这工作正常,但文本文件可能非常大,所以我想避免将整个 MemoryStream 转换为字符串,而是直接从流中提取所需的 xml 文本部分。

最好的方法是什么?

string xmlText;
using (var zip = ZipFile.Read(zipFileName))

    var ze = zip[zipPath];
    using (var ms = new MemoryStream())
    
        ze.Extract(ms);
        ms.Position = 0;
        using(var sr = new StreamReader(ms))
        
            xmlText = sr.ReadToEnd();
        
    


string startTag = "<someTag>";
string endTag = "</someTag>";
int startIndex = xmlText.IndexOf(startTag, StringComparison.Ordinal);
int endIndex = xmlText.IndexOf(endTag, startIndex, StringComparison.Ordinal) + endTag.Length - 1;
xmlText = xmlText.Substring(startIndex, endIndex - startIndex + 1);

【问题讨论】:

您可以从内存流中创建一个XmlReader,以避免将整个文件加载到内存中。 @juharr:把它写下来作为答案。另一种方法将是一种皇家痛苦,并且可能无法正常工作。 那是什么压缩库?您当前的方法将整个文件提取到 MemoryStream 中,因此可能会导致大文件出现内存不足异常。在 .NET 4.5 中ZipArchiveEntry.Open 可用于stream the file 它是 DotNetZip。我需要使用 .NET 4.0。 似乎您可以使用ZipEntry.OpenReader 而不是.Extract,然后在该流上使用XmlReader.Create 而不是MemoryStream 【参考方案1】:

如果您的文件是有效的 xml 文件,那么您应该能够使用 XmlReader 来避免将整个文件加载到内存中

string xmlText;
using (var zip = ZipFile.Read(zipFileName))

    var ze = zip[zipPath];
    using (var ms = new MemoryStream())
    
        ze.Extract(ms);
        ms.Position = 0;
        using (var xml = XmlReader.Create(ms))
        
            if(xml.ReadToFollowing("someTag"))
            
                xmlText = xml.ReadInnerXml();
            
            else
            
                // <someTag> not found
            
        
    

如果文件不是有效的 xml,您可能希望捕获潜在的异常。

【讨论】:

【参考方案2】:

假设由于它是 xml 它将有换行符,最好使用 StreamReader ReadLine 并在每一行中搜索您的标签。 (另请注意将您的 StreamReader 也放入 using 中。)

类似

        using (var ms = new MemoryStream())
        
            ze.Extract(ms);
            ms.Position = 0;
            using (var sr = new StreamReader(ms))
            
                bool adding = false;
                string startTag = "<someTag>";
                string endTag = "</someTag>";
                StringBuilder text = new StringBuilder();
                while (sr.Peek() >= 0)
                
                    string tmp = sr.ReadLine();
                    if (!adding && tmp.Contains(startTag))
                    
                        adding = true;
                    
                    if (adding)
                    
                        text.Append(tmp);
                    
                    if (tmp.Contains(endTag))
                        break;
                
                xmlText = text.ToString();
            
        

这假设开始和结束标签本身在一行上。如果没有,您可以像原来一样通过再次获取 start 和 end 的索引来清理生成的文本字符串。

【讨论】:

以上是关于从 MemoryStream 获取子字符串而不将整个流转换为字符串的主要内容,如果未能解决你的问题,请参考以下文章

从 Dask 数据帧中获取一行而不将整个数据帧加载到内存中

查找 JSON 对象大小而不将其解析为字符串

如何播放音频样本而不将其写入文件?

从Int获取单个数字而不使用字符串?

将 XDocument 转换为流

如何压缩整个文件夹(带有子文件夹)并通过 Flask 提供它而不将任何内容保存到磁盘