19. Operacje wejścia - wyjścia cz. 2

19. Operacje wejścia - wyjścia cz. 2

Autor: Paweł Kruczkowski

Opublikowano: 1/30/2007, 12:00 AM

Liczba odsłon: 46660

W poprzednim tygodniu poznaliśmy podstawowe operacje wejściowe oraz wyjściowe, jakie oferuje nam język obiektowy C# 2.0. Nauczyliśmy się już zapisywać dane do pliku oraz je czytać. Dzisiaj poszerzymy sobie jeszcze naszą wiedzę na w/w temat, ale de facto dzisiejszy artykuł będzie poświęcony głównie metodom i właściwościom umożliwiającym manipulację plików oraz katalogów.

Zanim przejdziemy do meritum przypomnijmy sobie poznane przez nas tydzień temu operacje. W tym celu napiszmy prosty programik:

using System;
using System.IO;
 
namespace CentrumXP_19
{
    class MojaKlasa
    {
        FileStream fsZapis, fsOdczyt;
      
        void Zapis()
        {
            fsZapis = new FileStream("C://mojPlik.txt", FileMode.Create);
            StreamWriter sw = new StreamWriter(fsZapis);
 
            Console.WriteLine("Podaj Twoj ulubiony kolor:");
            sw.WriteLine(Console.ReadLine());
            sw.Close();
 
            Console.WriteLine("Dane zostały zapisane do pliku tekstowego!");
        }
 
        void Odczyt()
        {
            string odczyt;
            fsOdczyt = new FileStream("C://mojPlik.txt", FileMode.Open);
            StreamReader sr = new StreamReader(fsOdczyt);
 
            Console.WriteLine("Odczytujemy dane z pliku.");
            while ((odczyt= sr.ReadLine()) != null)
            {
                Console.WriteLine("Twój ulubiony kolor to: {0}.", odczyt);
            }
            Console.WriteLine("Koniec odczytu z pliku tekstowego.");
        }
 
        static void Main()
        {
            MojaKlasa mk = new MojaKlasa();
            mk.Zapis();
            Console.WriteLine();
            mk.Odczyt();
        }
    }
}

Powyższy przykład jest przypomnieniem wiedzy, jaką zdobyliśmy w poprzednim tygodniu. Zdefiniowaliśmy sobie bowiem dwie metody, które odpowiednio zapisują do pliku tekstowego dane wprowadzone przez użytkownika (plik ten jest tworzony na dysku C pod nazwą: mojPlik.txt) oraz odczytują je z tegoż pliku. Obie te metody są wywołane w odpowiedni sposób w głównej metodzie statycznej Main().

Po skompilowaniu i uruchomieniu powyższego przykładu otrzymamy na ekranie następujące wyniki:

Znamy już przeznaczenie potężnej klasy FileStream, a także obiektów typu StreamWriter oraz StreamReader. Potrafimy już tworzyć pliki tekstowe (modyfikować je) i odczytywać z nich wpisane wcześniej informacje. Do tej pory jednak przedstawiliśmy bardzo prosty sposób tworzenia plików. Teraz chcemy poszerzyć tę wiedzę, która będzie stricte powiązana z manipulacją właśnie plików oraz katalogów, o których jak na razie nic nie pisaliśmy.

Potrzebne klasy do manipulacji plików i katalogów znajdują się w przestrzeni nazw System.IO. Są to między innymi: klasa File, która reprezentuje plik na dysku oraz klasa Directory, która odpowiada za różnego rodzaju działania na katalogach.

Klasa Directory udostępnia statyczne metody do tworzenia, sprawdzania poprawności oraz przemieszczenia się katalogów. Poniższa tabela przedstawia najważniejsze metody statyczne klasy Directory:

Nazwa metody Zastosowanie metody
CreateDirectory() Tworzy wszystkie katalogi i podkatalogi określone przez parametr path
GetCreationTime() Zwraca i ustawia czas tworzenia katalogu
GetDirectories() Pobiera określone katalogi
GetLogicalDrives() Zwraca nazwę napędu logicznego
Move() Przemieszcza katalog i jego zawartość do miejsca określonego przez path

Powyższe metody są statyczne, a więc nie trzeba tworzyć egzemplarza ich klasy tylko je wywoływać bezpośrednio.

Obok klasy Directory, drugą potężną klasą do manipulacji katalogami jest klasa DirectoryInfo. Klasa ta zawiera jedynie metody egzemplarza (nie ma bowiem żadnych metod statycznych).

Poniższa tabela przedstawia najczęściej używane przez programistów metody oraz właściwości klasy DirectoryInfo:

Metoda lub właściwość Zastosowanie metody
Attributes Właściwość, która pobiera i ustawia atrybuty aktualnego pliku
Exists Właściwość, która zwraca true, jeśli dany katalog istnieje
FullName Właściwość, która zwraca pełną ścieżkę do katalogu
Root Zwraca fragment ścieżki reprezentujący główny katalog
Create() Metoda, która pozwala utworzyć nowy katalog
CreateSubdirectory() Pozwala utworzyć podkatalog w określonej ścieżce
Delete() Pozwala usunąć katalog/podkatalog
GetDirectories() Zwraca listę podkatalogów
GetFiles() Zwraca listę plików, które znajdują się w danym katalogu
MoveTo() Pozwala przenieść katalog/podkatalog do odpowiedniego miejsca

Zastosujmy więc niektóre metody i właściwości z powyższej tabeli w następującym przykładzie:

using System;
using System.IO;
 
namespace CentrumXP_19
{
    class MojaKlasa
    {
        // definiujemy zmienna poziom i ustawiamy ja na -1
        int poziom = -1;
 
        public static void Main(string[] args)
        {
            MojaKlasa mk = new MojaKlasa();
 
            //wybieramy poczatkowy podkatalog
            string mojaSciezka = "C:\\KatalogGlowny";
            // definiujemy obiekt typu DirectoryInfo
            DirectoryInfo di = new DirectoryInfo(mojaSciezka);
            //wywolanie metody PrzegladKatalogow
            mk.PrzegladKatalogow(di);
        }
 
        //metoda, ktora wysietla informacje dotyczace odpowiedniego katalogu
        public void PrzegladKatalogow(DirectoryInfo dir)
        {
            //pierwszym poziomem bedzie 0
            poziom++;
            // wyswietlamy wszystkie katalogi i podkatalogi z okreslonej sciezki
            Console.WriteLine("Poziom: {0}, nazwa: {1}", poziom, dir.Name);
            // wrzucamy do tablicy wszystkie katalogi aktualnego folderu
            DirectoryInfo[] directories = dir.GetDirectories();
//przechodzimy foreachem po wszystkich katalogach jakie aktualnie znajduja sie w tablicy
            foreach (DirectoryInfo di in directories)
            {
                //rekurencyjne wywolanie metody PrzegladKatalogow()
                PrzegladKatalogow(di);
            }
            // zmniejszamy poziom
            poziom--;
        }
    }
}

Powyższy programik to rekurencyjne zagłębianie się w podkatalogi. Na początku określamy katalog, dla którego będziemy wyświetlać wszystkie podkatalogi, jakie on posiada (w naszym przypadku będzie to KatalogGlowny, leżący na dysku C, który będzie miał 3 katalogi, z których jeden będzie miał jeszcze jeden własny podkatalog). Następnie dla tego głównego katalogu definiujemy obiekt di typu DirectoryInfo, po czym wywołujemy na nim metodę PrzegladKatalogow(), przekazując do niej utworzony wcześniej obiekt di. Zadaniem tej metody jest wyświetlenie wszystkich katalogów, jakie posiada KatalogGlowny, a następnie pobranie dla nich wszystkich ich podkatalogów. Liczba wszystkich podkatalogów aktualnego katalogu jest wyświetlana za pomocą metody GetDirectories(), która zwraca tablicę obiektów typu DirectoryInfo.

W instrukcji foreach „przechodzimy” po wszystkich podkatalogach aktualnego katalogu i..wywołujemy na nowo metodę PrzegladKatalogow() przekazując jej odpowiedni, aktualny podkatalog. Taki mechanizm nazywamy rekurencją. Innymi słowy, takie podejście powoduje rekurencyjne zagłębianie się metody w każdy podkatalog, a następnie przejście do następnego podkatalogu z tego samego poziomu.

Po skompilowaniu i uruchomieniu powyższego przykładu otrzymamy następujące wyniki:

Potrafimy już używać obiektów DirectoryInfo, a więc czas na poznianie drugiej ogromnej klasy, jaką jest niewątpliwie klasa File oraz jej imienniczka FileInfo. Podobnie jak w przypadku Directory i DirectoryInfo, tak i tutaj klasa File udostępnia statyczne metody do tworzenia, sprawdzania poprawności oraz przemieszczenia się plików, natomiast klasa FileInfo zawiera odpowiednie metody egzemplarza.

Poniższa tabela zawiera wybrane, publiczne metody statyczne klasy File:

Nazwa metody Zastosowanie metody
Copy() Metoda do kopiowania istniejącego pliku do nowego pliku
Create() Tworzy nowy plik w miejscu, które określiliśmy w parametrze path
Delete() Usuwamy określony plik
Exists Właściwość, która zwraca true, jeśli dany plik istnieje
GetCreationTime() Metoda do pobierania daty utworzenia danego pliku

Natomiast poniższa tabela prezentuje wybrane metody klasy FileInfo:

Metoda lub właściwość Zastosowanie metody
Attributes Właściwość, która pobiera i ustawia atrybuty aktualnego pliku
Exists Właściwość, która zwraca true, jeśli dany plik istnieje
FullName Właściwość, która zwraca pełną ścieżkę do pliku
LastWriteTime Pobiera lub ustawia czas ostatniego dostępu
Lenght Zwraca rozmiar danego pliku
Name Zwraca nazwę danego pliku
Create() Tworzy nowy plik
Delete() Usuwa dany plik
Open() Otwiera dany plik z różnymi opcjami odczytu, zapisu etc.

Wykorzystajmy teraz kilka metod oraz właściwości z powyższej tabeli i napiszmy program, który oprócz wyświetlania wszsytkich katalogów (podkatalogów) będzie również zwracał listę plików, jakie znajdują się w odpowiednich katalogach i podkatalogach:

using System;
using System.IO;
 
namespace CentrumXP_19
{
    class MojaKlasa
    {
        // definiujemy zmienna poziom i ustawiamy ja na -1
        int poziom = -1;
 
        public static void Main(string[] args)
        {
            MojaKlasa mk = new MojaKlasa();
 
            //wybieramy poczatkowy podkatalog
            string mojaSciezka = "C:\\KatalogGlowny";
            // definiujemy obiekt typu DirectoryInfo
            DirectoryInfo di = new DirectoryInfo(mojaSciezka);
            //wywolanie metody PrzegladKatalogow
            mk.PrzegladKatalogow(di);
        }
 
        //metoda, ktora wysietla informacje dotyczace odpowiedniego katalogu
        public void PrzegladKatalogow(DirectoryInfo dir)
        {
            // pierwszym poziomem bedzie 0
            poziom++;
            // wyswietlamy wszystkie katalogi i podkatalogi z okreslonej sciezki
            Console.WriteLine("Poziom: {0}, nazwa katalogu: {1}", poziom, dir.Name);
            // pobieramy wszsytkie pliki, jakie znajduja sie w danym aktualnym katalogu
            FileInfo[] fi = dir.GetFiles();
            //przechodzimy foreachem po wszsytkich plikach jakie znajduja sie w danym aktualnym katalogu
            foreach (FileInfo plik in fi)
            {
                Console.WriteLine("Poziom: {0}, nazwa pliku: {1}, rozmiar: {2}, czas ostatniej modyfikacji: {3}", poziom, plik.Name, plik.Length, plik.LastWriteTime);
            }
            // wrzucamy do tablicy wszystkie katalogi aktualnego folderu
            DirectoryInfo[] directories = dir.GetDirectories();
            //przechodzimy foreachem po wszystkich katalogach jakie aktualnie znajduja sie w tablicy
            foreach (DirectoryInfo di in directories)
            {
                //rekurencyjne wywolanie metody PrzegladKatalogow()
                PrzegladKatalogow(di);
            }
            // zmniejszamy poziom
            poziom--;
        }
    }
}

Powyższy przykład wyświetla nie tylko listę wszystkich katalogów oraz podkatalogów z określonej ścieżki, ale również listę wszystkich plików, jakie znajdują się w tych folderach. W tym celu zdefiniowaliśmy sobie tablicę przechowującą obiekty FileInfo (listę plików) aktualnego katalogu (podkatalogu), a następnie za pomocą instrukcji foreach przechodzimy po wszystkich tych plikach i wyświetlamy odpowiednie na ich temat informacje.

Po uruchomieniu powyższego przykładu otrzymamy następujące wyniki:

Na koniec napiszemy program, który będzie podsumowaniem wiedzy, jaką zdobyliśmy dzisiaj oraz tydzień temu. Nasz program będzie tworzył na dysku C:\ naszego komputera folder a w nim plik, w którym będzie można napisać komentarz na temat portalu CentrumXP. Następnie z pliku tego będziemy odczytywać wpisany tekst i wyświetlać na ekranie komputera:

using System;
using System.IO;
 
namespace CentrumXP_19
{
    class MojaKlasa
    {
        static void Main(string[] args)
        {
            //tworzymy katalog wraz z plikiem
            MojaKlasa mk = new MojaKlasa();
            Console.WriteLine(@"Podaj nazwę folderu jaki chcesz stworzyć na dysku C:\");
            string nazwaFolderu = Console.ReadLine();
            mk.NowyFolder(@"C:\" + nazwaFolderu);
            Console.WriteLine("Stworzono folder: {0}", nazwaFolderu);
Console.WriteLine(@"Podaj nazwę pliku jaki chcesz stworzyć w folderze: {0}:", nazwaFolderu);
            string nazwaPliku = Console.ReadLine();
FileStream fs = new FileStream(@"C:\" + nazwaFolderu + @"\" + nazwaPliku, FileMode.Create);
            //wpisujemy do pliku tekst
            Console.WriteLine("Napisz swój komentarz na temat portalu CentrumXP:");
            StreamWriter sw = new StreamWriter(fs);
            sw.WriteLine(Console.ReadLine());
            sw.Close();
            Console.WriteLine("Twój komentarz został zapisany do pliku: {0}", nazwaPliku);
            //uruchamiamy metode, ktora odczyta z pliku wpisany komentarz
            DirectoryInfo di = new DirectoryInfo(@"C:\" + nazwaFolderu);
            mk.MojaMetoda(di);
        }
 
        void NowyFolder(string sciezka)
        {
            DirectoryInfo di = new DirectoryInfo(sciezka);
            di.Create();
        }
 
        void MojaMetoda(DirectoryInfo dirInfo)
        {
            string mojeDaneOdczyt = string.Empty;
            FileInfo[] fileInfo = dirInfo.GetFiles();
          
            foreach (FileInfo fi in fileInfo)
            {
                FileStream fs = new FileStream(fi.FullName, FileMode.Open);
           
                StreamReader sr = new StreamReader(fs);
           
                Console.WriteLine("Odczytujemy Twój komentarz.");
                Console.WriteLine("Twój komentarz to:");
 
                while ((mojeDaneOdczyt = sr.ReadLine()) != null)
                {
                    Console.WriteLine(mojeDaneOdczyt);
                }
                sr.Close();
            }
        }
       
    }
}

Powyższy przykład jest podsumowaniem poznanych przez nas dotychczas metod oraz właściwości, jakie najczęściej używane są przez programistę w czasie wykonywania różnorakich operacji wejścia – wyjścia. Mamy nadzieję, że przedstawione przez nas podstawy na w/w temat przyczynią się do rozpoczęcia ich używania w naszych programach oraz sprawią, że nie będą dla nas obce nawet w trudniejszych przykładach.

Po uruchomieniu programu otrzymamy następujący przykładowy ekran:

Za tydzień opowiemy sobie o atrybutach oraz mechaniźmie refleksji w języku obiektowym C# 2.0.