2013-06-26 16 views
5

Zacząłem robić coś w następujący sposób:Skuteczny sposób pisać wiele linii w pliku tekstowym

using (TextWriter textWriter = new StreamWriter(filePath, append)) 
{ 
    foreach (MyClassA myClassA in myClassAs) 
    { 
     textWriter.WriteLine(myIO.GetCharArray(myClassA)); 

     if (myClassA.MyClassBs != null) 
      myClassA.MyClassBs.ToList() 
       .ForEach(myClassB => 
        textWriter.WriteLine(myIO.GetCharArray((myClassB))); 

     if (myClassA.MyClassCs != null) 
      myClassA.MyClassCs.ToList() 
       .ForEach(myClassC => 
        textWriter.WriteLine(myIO.GetCharArray(myClassC))); 
    } 
} 

ten wydawał się dość wolno (~ 35 sekund do 35000 wierszy).

Potem próbowałem wykonać przykład here, aby utworzyć bufor z następującym kodem, ale nic mnie to nie zyskało. Wciąż widziałem czasy około 35 sekund. Czy wystąpił błąd w sposobie zaimplementowania bufora?

using (TextWriter textWriter = new StreamWriter(filePath, append)) 
{ 
    char[] newLineChars = Environment.NewLine.ToCharArray(); 
    //Chunk through 10 lines at a time. 
    int bufferSize = 500 * (RECORD_SIZE + newLineChars.Count()); 
    char[] buffer = new char[bufferSize]; 
    int recordLineSize = RECORD_SIZE + newLineChars.Count(); 
    int bufferIndex = 0; 

    foreach (MyClassA myClassA in myClassAs) 
    { 
     IEnumerable<IMyClass> myClasses = 
      new List<IMyClass> { myClassA } 
       .Union(myClassA.MyClassBs) 
       .Union(myClassA.MyClassCs); 

     foreach (IMyClass myClass in myClasses) 
     { 
      Array.Copy(myIO.GetCharArray(myClass).Concat(newLineChars).ToArray(), 
       0, buffer, bufferIndex, recordLineSize); 

      bufferIndex += recordLineSize; 

      if (bufferIndex >= bufferSize) 
      { 
       textWriter.Write(buffer); 

       bufferIndex = 0; 
      } 
     } 
    } 

    if (bufferIndex > 0) 
     textWriter.Write(buffer); 
} 

Czy jest lepszy sposób na osiągnięcie tego?

+0

Czy próbowałeś użyć StringBuilder i zbudować ciąg, a następnie po prostu napisz raz do pliku. – Sorceri

+0

jakikolwiek szczególny powód użycia buforów? – JustCode

+8

Czy jesteś pewien, że to naprawdę jest to problem? Co się stanie, jeśli po prostu napiszesz do 'StreamWriter' owiniętego na przykład' MemoryStream'? –

Odpowiedz

0

Zrzuciłem prosty fragment, który moim zdaniem jest nieco bardziej przejrzysty; ale, znowu, nie jestem do końca pewien, co próbujesz osiągnąć. Ponadto nie mam dostępnych żadnych zajęć, więc nie mogę wykonać żadnych testów.

Ta próbka w zasadzie to samo, co masz; oprócz tego, że używa pewnych ogólnych metod i robi to wszystko w jednym miejscu.

string filePath = "MickeyMoust.txt"; 
bool append = false; 
List<MyClassA> myClassAs = new List<MyClassA> { new MyClassA() }; 
    List<char[]> outputLines = new List<char[]>(); 

foreach (MyClassA myClassA in myClassAs) 
{ 
    outputLines.Add(myIO.GetCharArray(myClassA)); 

    if (myClassA.MyClassBs != null) 
     outputLines.AddRange(myClassA.MyClassBs.Select(myClassB => myIO.GetCharArray(myClassB))); 

    if (myClassA.MyClassCs != null) 
     outputLines.AddRange(myClassA.MyClassCs.Select(myClassC => myIO.GetCharArray(myClassC))); 
} 

var lines = outputLines.Select(line => string.Concat<char>(line)); 
if (append) 
    File.AppendAllLines(filePath, lines); 
else 
    File.WriteAllLines(filePath, lines); 

Oto wersja StringBuilder:

string filePath = "MickeyMoust.txt"; 
bool append = false; 
List<MyClassA> myClassAs = new List<MyClassA> { new MyClassA() }; 
StringBuilder outputLines = new StringBuilder(); 

foreach (MyClassA myClassA in myClassAs) 
{ 
    outputLines.Append(myIO.GetCharArray(myClassA)); 

    if (myClassA.MyClassBs != null) 
     myClassA.MyClassBs.ForEach(myClassB=>outputLines.Append(myClassB)); 

    if (myClassA.MyClassCs != null) 
     myClassA.MyClassCs.ForEach(myClassC => outputLines.Append(myClassC)); 
} 

if (append) 
    File.AppendAllText(filePath, outputLines.ToString()); 
else 
    File.WriteAllText(filePath, outputLines.ToString()); 
+0

Próbowałem tego i widziałem poprawę 2 - 3 sekund, więc jest to lepsze niż to, co miałem. Dziękuję za udostępnienie. – lintmouse

+0

Jak powiedzieli niektórzy inni, możesz spróbować użyć StringBuilder i dołączyć test, w przeciwieństwie do używania listy, a następnie napisać ToString() StringBuildera jako pojedynczy zapis. Nie wiem, ile/jeśli to w ogóle poprawiłoby. –

4

I podejrzewam, że większość swojego czasu spędza nie w I/O. Nie ma mowy, aby zapisanie 35 000 linii zajęło 35 sekund, chyba że te linie są długie.

Najprawdopodobniej większość czasu spędza się w metodzie GetCharArray, cokolwiek to robi.

Kilka sugestii:

Jeśli naprawdę myślisz I/O jest problem, należy zwiększyć rozmiar bufora strumienia. Zadzwoń pod numer StreamWriter constructor, który pozwala określić rozmiar bufora. Na przykład:

using (TextWriter textWriter = new StreamWriter(filePath, append, Encoding.Utf8, 65536)) 

To będzie działać lepiej niż domyślny rozmiar bufora 4K. Zwiększenie rozmiaru bufora o ponad 64K nie jest na ogół przydatne i może obniżyć wydajność.

Nie linie przed buforem lub nie dołączaj do StringBuilder. To może dać ci niewielki wzrost wydajności, ale przy ogromnych kosztach związanych ze złożonością. Niewielkie zwiększenie wydajności nie jest warte koszta utrzymania.

Skorzystaj z foreach. Masz ten kod:

if (myClassA.MyClassBs != null) 
    myClassA.MyClassBs.ToList() 
     .ForEach(myClassB => 
      textWriter.WriteLine(myIO.GetCharArray((myClassB))); 

który ma stworzyć konkretną listę z co MyClassBs kolekcji jest, a następnie wyliczyć ją. Dlaczego po prostu nie wyliczać rzeczy bezpośrednio:

if (myClassA.MyClassBs != null) 
{ 
    foreach (var myClassB in myClassA.MyClassBs) 
    { 
     textWriter.WriteLine(myIO.GetCharArray((myClassB))); 
    } 
} 

To pozwoli Ci zaoszczędzić pamięć wymaganą przez ToList, a czas potrzebny do wyliczenia kolekcję podczas tworzenia listy.

Wszystko, co powiedzieliśmy, jest prawie pewne, że twoja metoda GetCharArray jest tym, co zajmuje cały czas. Jeśli naprawdę chcesz przyspieszyć swój program, spójrz tam. Próba optymalizacji zapisu do StreamWriter to strata czasu. Nie osiągniesz znaczącego wzrostu wydajności.

+1

Powinieneś prawdopodobnie unikać używania [.forAach() anyway] (http://blogs.msdn.com/b/ericlippert/archive/2009/05/18/foreach-vs-foreach.aspx). –

Powiązane problemy