70-536 Reading and Writing Files and Streams

Poniższy artykuł pochodzi z serii Przygotowań do egzaminu 70-536.

Można użyć wielu klas Stream do zapisu i odczytu plików. .NET Framework dostarcza nam specjalne klasy do zapisu plików tekstowych, plików binarnych czy też do kompresji danych i przechowywania ich w pamięci. W dzisiejszym artykule przyjrzymy się temu tematowi w mniej lub bardziej szczegółowy sposób.

Odczyt i zapis pliku tekstowego

Do odczytu pliku tekstowego możemy użyć klasy TextReader lub StreamReader. Przykłady ich użycia (zaczerpnięte z training kit’a bo jak wiecie nie lubie wymyślać sztucznych tworów ;))

   1: TextReader tr = File.OpenText(@"C:\windows\win.ini");
   2: Console.Write(tr.ReadToEnd());
   3: tr.Close();
   4:  
   5:  
   6: StreamReader sr = new StreamReader(@"C:\windows\win.ini");
   7: string input;
   8: while ((input = sr.ReadLine()) != null )
   9: Console.WriteLine(input);
  10: sr.Close();

 

Te dwie oddzielne sekcje kodu robią identyczne rzeczy czyli wczytują plik win.ini i wyświetlają go na ekran. Warto jednak wiedzieć, że klasa StreamWriter dziedziczy po klasie TextReader stąd w klasie StreamWriter równie dobrze można użyć statycznej metody File.OpenText lub ścieżkę do pliku przekazać w konstruktorze tak jak w przykładzie.

Jeśli chodzi o zapis do pliku tekstowego to również nie jest to wielka filozofia. Spójrzmy na przykład:

   1: TextWriter tw = File.CreateText("output.txt");
   2: tw.WriteLine("Hello, world!");
   3: tw.Close();

Tutaj warto wspomnieć o metodzie Flush() która w klasie TextWriter czyści bufor związany z aktualnym procesem zapisu i powoduje zapis danych umieszczonych w buforze. Dzięki niej możemy być pewni, że dane zostaną zapisane na dysku bez zamknięcia pliku zapisu. Bo co jeśli raptem wyłączy się nam komputer? Mamy utracić wszystkie dane które nie zapisały się tylko dlatego, że nie wywołaliśmy metody Close() ?

Użycie MemoryStream

Klasa ta jest wykorzystywana do tymczasowego przechowywania danych które później możemy ewentualnie zapisać do pliku. Dane są składowane w pamięci a nie w fizycznym pliku. Stąd po otwarciu strumienia możemy do niego dodawać różne dane aktualizować a później zapisać wszystko za jednym zamachem. Pozwala nam to zaoszczędzić czas bo nie musimy ciągle robić zapisu i odczytu pliku a ponad to w sytuacji kiedy plik jest używany przez różne procesy unikamy potencjalnych konfliktów. Kolejny przykład zaczerpnięty wiadomo skąd…

   1: MemoryStream ms = new MemoryStream();
   2: StreamWriter sw = new StreamWriter(ms);
   3:  
   4: sw.WriteLine("Hello, World!");
   5: sw.Flush();
   6:  
   7: ms.WriteTo(File.Create("memory.txt"));
   8:  
   9: sw.Close();

 

W powyższym przykładzie zademonstrowano sposób zapisu danych do MemoryStream które są następnie zapisywane w pliku txt. Dla ułatwienia zapisu do MemoryStream użyto klasy StreamWriter. Bez niej moglibyśmy użyć tylko metod WriteByte (pozwala zapisać bajt), Write (zapis tablicy bajtów) oraz ReadByte i Read analogicznie do metod zapisu.

Użycie BufferedStream

Użycie klasy BufferedStream z obiektem klasy FileStream jest zbędne z tego względu, że te obiekty zawierają dokładnie tą samą logike buforwania danych i użycie jej jest bezużyteczne. Jest to specjalna klasa którą możemy implementować dla swoich niestandardowych rozwiązań. Klasy Stream mają już zaimplementowaną logikę buforowania. Klasy BufferedStream możemy używać podobnie jak MemoryStream bo tak samo jak ona przechowuje strumień danych które później możemy zapisać w pamięci trwałej.

Korzystanie ze strumieni kompresujących

Możemy użyć w .NET Framework specjalnych strumieni do kompresji danych które oczywiście pozwalają nam zaoszczędzić pamięć. Za chwilę przykład kodu który kompresuję i dekompresuje plik tekstowy przy pomocy GZipStream. Musimy mieć na uwadze, że klasa ta podobnie jak np. MemoryStream pracuje bezpośrednio na bajcie lub tablicy bajtów. Stąd jeżeli nie chcemy pracować na bajtach musimy znowu posłużyć się tak jak w poprzednim przykładzie klasą StreamWriter i StreamReader.

   1: GZipStream gzOut = new GZipStream(File.Create("data.zip"),CompressionMode.Compress);
   2:  
   3: StreamWriter sw = new StreamWriter(gzOut);
   4:  
   5: for (int i = 1; i < 1000; i++)
   6: sw.Write("Hello, World! ");
   7:  
   8: sw.Close();
   9: gzOut.Close();
  10:  
  11: GZipStream gzIn = new GZipStream(File.OpenRead("data.zip"),CompressionMode.Decompress);
  12:  
  13: StreamReader sr = new StreamReader(gzIn);
  14: Console.WriteLine(sr.ReadToEnd());
  15:  
  16: sr.Close();
  17: gzIn.Close();

Skompresowany plik zajmuje na dysku 289 bajtów…użycie samej klasy FileStream utworzyło by plik na poziomie 13,986 bajtów.

Isolated Storage

Izolowaną przestrzeń w pamięci możemy wykorzystywać w podobny sposób jak to robiliśmy z klasą np. StreamReader czy StreamWriter do odczytu i zapisu pliku. Dzięki Isolated Storage możemy po stornie klienta zapisać dane dotyczące aplikacji czy użytkownika. Nie jest oczywiście zalecane ze względu na bezpieczeństwo zapisywanie tam informacji poufnych takich jak np. niezaszyfrowane klucze, hasła itd. Istotną rzeczą jest to, że dostęp do takiego pliku jest tylko po stornie klienta który go stworzył. Klasy do pracy z Isolated Storage to: IsolatedStorageFile, IsolatedStorageFileStream oraz IsolatedStorageException. Wszystkie oczywiście z przestrzeni nazw System.IO.IsolatedStorage.

Żeby chociaż trochę się rozjaśniło wrzucę przykład wraz z oryginalnymi komentarzami do przeanalizowania jako praca domowa ;)

   1: // Get the store isolated by the assembly
   2: IsolatedStorageFile isoStore =
   3: IsolatedStorageFile.GetUserStoreForAssembly();
   4: // Create the isolated storage file in the assembly we just grabbed
   5: IsolatedStorageFileStream isoFile = new
   6: IsolatedStorageFileStream("myfile.txt", FileMode.Create, isoStore);
   7: // Create a StreamWriter using the isolated storage file
   8: StreamWriter sw = new StreamWriter(isoFile);
   9: // Write a line of text to the file
  10: sw.WriteLine("This text is written to a isolated storage file.");
  11: // Close the file
  12: sw.Close();

I kod do odczytu tego co zapisaliśmy w poprzednim przykładzie:

   1: // Get the store isolated by the assembly
   2: IsolatedStorageFile isoStore =
   3: IsolatedStorageFile.GetUserStoreForAssembly();
   4: // Open the isolated storage file in the assembly we just grabbed
   5: IsolatedStorageFileStream isoFile = new
   6: IsolatedStorageFileStream("myfile.txt", FileMode.Open, isoStore);
   7: // Create a StreamReader using the isolated storage file
   8: StreamReader sr = new StreamReader(isoFile);
   9: // Read a line of text from the file
  10: string fileContents = sr.ReadLine();
  11: // Close the file
  12: sr.Close();

Znowu artykuł mi się wydłużył ale dzięki, że doczytałeś do tego momentu drogi czytelniku ;) Następny mój artykuł będzie o kolekcjach i słownikach natomiast z tego co się orientuje ogólnie kolejny artykuł będzie o wyrażeniach regularnych.

Kolejny artykuł z serii to 70-536: Forming Regular Expressions

Tagi: , , ,

Comments (1) -

Gregor
Gregor Poland
10/4/2010 1:31:11 PM Permalink

StreamWriter dziedziczy po TextWriter, nie TextReader

Pingbacks and trackbacks (1)+

Add comment




  Country flag
biuquote
  • Comment
  • Preview
Loading


Eastgroup.pl na facebooku