2017-01-03 3 views
3

Je voudrais être en mesure d'obtenir efficacement une sous-chaîne d'un MemoryStream (qui vient à l'origine d'un fichier xml dans un zip). Actuellement, je lis l'ensemble MemoryStream à une chaîne, puis rechercher les balises de début et de fin du nœud xml que je désire. Cela fonctionne bien, mais le fichier texte peut être très grand, donc je voudrais éviter de convertir l'ensemble MemoryStream dans une chaîne et au lieu simplement extraire la section souhaitée du texte XML directement à partir du flux.Obtenez de MemoryStream sans substring convertir flux entier en chaîne

Quelle est la meilleure façon de procéder?

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); 
+2

Vous pouvez créer un 'XmlReader' du flux de mémoire pour éviter de charger l'intégralité du fichier en mémoire. – juharr

+1

@juharr: Ecrivez cela comme réponse. L'autre façon va être une douleur royale et probablement ne pas fonctionner correctement. – Joshua

+1

quelle bibliothèque zip est-ce? Votre méthode actuelle extrait le fichier entier dans MemoryStream, ce qui peut entraîner une exception d'insuffisance de mémoire pour les gros fichiers. Dans .NET 4.5 [ 'ZipArchiveEntry.Open'] (https://msdn.microsoft.com/en-us/library/system.io.compression.ziparchiveentry.open) peut être utilisé pour [diffuser le fichier] (http: //www.dotnetcurry.com/csharp/974/zip-archives-csharp-dotnet) – Slai

Répondre

2

Si votre fichier est un fichier XML valide alors vous devriez être en mesure d'utiliser un XmlReader pour éviter de charger le fichier en mémoire

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 
      } 
     } 
    } 
} 

Vous voudrez probablement d'intercepter les exceptions possibles si le fichier est pas xml valide.

1

En supposant que, puisqu'il est xml, il aura des sauts de ligne, il serait probablement préférable d'utiliser StreamReader ReadLine et rechercher vos mots-clés dans chaque ligne. (Notez également mettre votre StreamReader en utilisant aussi bien.)

Quelque chose comme

 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(); 
      } 
     } 

Cela suppose que les balises de début et de fin sont sur une ligne par eux-mêmes. Sinon, vous pouvez nettoyer la chaîne de texte qui en résulte en obtenant l'index de début et de fin comme vous l'avez fait à l'origine.