[C#] Obsługa błędów I/O 11732 22

O temacie

Autor MajkeI

Zaczęty 26.05.2015 roku

Wyświetleń 11732

Odpowiedzi 22

MajkeI

MajkeI

Użytkownicy
Front End Developer
posty698
Propsy169
Profesjabrak
  • Użytkownicy
  • Front End Developer

MajkeI

[C#] Obsługa błędów I/O
2015-05-26, 21:26(Ostatnia zmiana: 2015-05-26, 21:55)
Napisałem prosty program konsolowy - podajemy ścieżkę i zamienia nazwy wszystkich plików w danym katalogu na takie same, tylko z wielkich liter.

Spoiler
using System;
using System.IO;

namespace Program {
    class Program {
        static void Main(string[] args) {
            string[] filePaths;
            while (true) {
                Console.Clear();
                Console.WriteLine("Input directory");
                filePaths = Directory.GetFiles(Console.ReadLine());

                if (filePaths.Length == 0) {
                    Console.WriteLine("No files in this directory!");
                } else {
                    Console.Clear();
                    Console.WriteLine("Changed files: ");

                    for (int i = 0; i < filePaths.Length; i++)
                    {
                        File.Move(filePaths[i], filePaths[i].ToUpper());
                        filePaths[i] = Path.GetFileName(filePaths[i]);
                        Console.WriteLine((i + 1) + ". " + filePaths[i]);
                    }
                }
               
                Console.ReadLine();
            }
        }
    }
}

Moje pytanie - jak zrobić obsługę błędów? Chodzi mi o przypadek, gdzie podamy błędną ścieżkę do folderu.
Znalazłem jakieś "DirectoryNotFoundException" jednak nie umiem z tego skorzystać. Ktoś pomoże?

EDIT: Ok, wykombinowałem - tylko, czy pod względem "czystości kodu" jest to dobrze zrobione?
Spoiler
using System;
using System.IO;

namespace Program
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] filePaths;
            while (true)
            {
                Console.Clear();
                Console.WriteLine("Input directory");
                try
                {
                    filePaths = Directory.GetFiles(Console.ReadLine());

                    if (filePaths.Length == 0)
                        Console.WriteLine("No files in this directory!");
                    else
                    {
                        Console.Clear();
                        Console.WriteLine("Changed files: ");

                        for (int i = 0; i < filePaths.Length; i++)
                        {
                            File.Move(filePaths[i], filePaths[i].ToUpper());
                            filePaths[i] = Path.GetFileName(filePaths[i]);
                            Console.WriteLine((i + 1) + ". " + filePaths[i]);
                        }
                    }
                }
                catch (DirectoryNotFoundException)
                {
                    Console.WriteLine("Directory is incorrect!");
                }
                Console.ReadLine();
            }
        }
    }
}

Hmm to tak przy okazji - jak zrobić, żeby przeszukiwało również podfoldery podanego katalogu?
 

inż. Avallach

inż. Avallach

Administrator
posty7662
Propsy5238
NagrodyV
ProfesjaProgramista
  • Administrator
To nie jest czysty kod (ale dobrze że chociaż znasz to pojęcie).

Wyjątki, jak nazwa wskazuje, służą do obsługi sytuacji wyjątkowych. Takich których nie da się z góry przewidzieć i które normalnie raczej się nie powinny zdarzać. Czy podanie przez użytkownika złej ścieżki do folderu jest taką? Nie. To dość typowa sytuacja i można łatwo ją przewidzieć zanim spróbujesz coś z tego folderu odczytać (co spowodowałoby wyjątek). Po prostu użyj metody Directory.Exists(string path). Jeśli folder nie istnieje, to męcz użytkownika (nawet i do skutku) o podanie poprawnej ścieżki. A dopiero kiedy masz ścieżkę która istnieje, to z niej czytaj.

Ogólna zasada jest taka: nie próbuj obsługiwać wyjątków, których wystąpieniu możesz zapobiec. Prawie zawsze zapobieżenie wyjątkowi jest lepsze dla programu niż łapanie go. Sama próba łapania wyjątku jest darmowa. Jeśli wyjątek nie wystąpi, program nic nie "płaci". Ale jeśli już wystąpi, to cena jest nieprzyjemna i lepiej jej unikać.

Co do przeszukiwania katalogów - wyciągnij z tego osobną metodę i wywołuj ją rekursywnie na każdym podkatalogu znalezionym w obecnie przerabianym katalogu.

Z innych uwag - masz pętlę while (true) i żadnego wyjścia z niej. To bardzo nieeleganckie - jedynym sposobem na wyjście z takiej aplikacji jest zabicie jej.

MajkeI

MajkeI

Użytkownicy
Front End Developer
posty698
Propsy169
Profesjabrak
  • Użytkownicy
  • Front End Developer
Ok, zgodnie z Twoimi wskazówkami:
Spoiler
using System;
using System.IO;
namespace Program
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] filePaths;
            ConsoleKeyInfo key;
            while (true)
            {
                Console.Clear();
                Console.WriteLine("Input directory");
                string path = Console.ReadLine();
                if (!Directory.Exists(path))
                    Console.WriteLine("Directory is incorrect!");
                else
                {
                    filePaths = Directory.GetFiles(path);
                    if (filePaths.Length == 0)
                        Console.WriteLine("No files in this directory!");
                    else
                        Console.WriteLine("Changed files: ");
                    for (int i = 0; i < filePaths.Length; i++)
                    {
                        File.Move(filePaths[i], filePaths[i].ToUpper());
                        filePaths[i] = Path.GetFileName(filePaths[i]);
                        Console.WriteLine((i + 1) + ". " + filePaths[i]);
                    }
                }
                Console.WriteLine("\n(Press ESCAPE to exit)");
                key = Console.ReadKey();
                if (key.Key == ConsoleKey.Escape)
                    return;
            }
        }
    }
}
Czy teraz jest ok? Da się z tego zrobić "clean code"? Na czym to polega?
A co mam zrobić jak chodzi o przeszukiwanie tych podfolderów?
 

inż. Avallach

inż. Avallach

Administrator
posty7662
Propsy5238
NagrodyV
ProfesjaProgramista
  • Administrator
Nie musisz robić zmiennej key jeśli nie chcesz używać informacji o klawiszu nigdzie indziej. Wystarczy sprawdzić warunek:
if (Console.ReadKey().Key == ConsoleKey.Escape)Zmienną wywal.
No i cytuję:
Co do przeszukiwania katalogów - wyciągnij z tego osobną metodę i wywołuj ją rekursywnie na każdym podkatalogu znalezionym w obecnie przerabianym katalogu.
Wyciąganie metod jest ściśle związane z pojęciem czystego kodu. Czy możesz w zwięzłym zdaniu powiedzieć co robi twoja metoda? Nie, bo w tej jednej metodzie jest cały twój program. Ona robi dużo różnych rzeczy składających się z mniejszych czynności. Tak nie powinno być. Jeśli potrafisz określić z jakich kroków to się składa, to naprawdę podziel ją na takie kroki. Jutro jak będę miał chwilę pokażę ci jak bym to zrobił, na razie spróbuj sam. Kodu nie musisz zmieniać, chodzi o poprzestawianie go do osobnych metod tam gdzie ma to sens (takie miejsce jest co najmniej jedno).

Sawik

Sawik

Użytkownicy
Rebel
posty4772
Propsy3197
ProfesjaNierób
  • Użytkownicy
  • Rebel
Avallach popraw mnie jeśli się mylę: 
W czystym kodzie metoda odpowiada za określoną czynność np. kopiuj tekst, zapisz do pliku, dzięki czemu nazewnictwo jest proste i logiczne. 
 
Życzę wam seksu analnego po stronie biernej.
Dropbox +500 mb na start
LowPoly
Wykonanie modelu niskopoligonowego to sztuka kompromisu. Nie jest to jedynie uproszczenie modelu wysokopoligonowego, ale głęboka modyfikacja oraz podejmowanie decyzji często zmieniających wygląd pierwotny obiektu, tak by przy najmniejszej ilości trójkątów uzyskać jak najwierniej odwzorowany kształt oryginału. Nie można też zapomnieć o tym iż musi nadal wyglądać przekonywająco i tak balansować by uzyskać efekt optymalny.

Podstawowym założeniem jest, że model nie powinien mieć zbędnych, niewidocznych dla gracza detali włączonych w geometrie. Większość obiektów jakie znajdują się w grze powinna prezentować się najlepiej z odległości około 3-5 metrów. Wszelkie detale, które zanikają, wydają się płaskie lub zlewają się z bryłą modelu należy uznać za zbędne i pozostawić je na normal mapie.

Fakt, iż gracz będzie w stanie podejść bliżej do obiektu i zobaczyć go z mniejszej niż 3m odległości nie powinno stanowić większego problemu, gdyż większą rolę odgrywają wtedy tekstury oraz dodatkowy detal zależny od materiału obiektu. To właśnie kompromis między wydajnością, a szczegółowością otoczenia.

Detal, którego nie widać z 3-5 metrów nie powinnien istnieć w geometrii modelu.
Krawędzie znajdujące się blisko siebie, które zlewają się z większej odległości należy uprościć do wspólnej płaszczyzny

MajkeI

MajkeI

Użytkownicy
Front End Developer
posty698
Propsy169
Profesjabrak
  • Użytkownicy
  • Front End Developer
Ok, sorry nie zauważyłem. Jak wrócę do domu to zrobię
 

Adanos

Adanos

Administrator
Szara eminencja
posty5204
Propsy3870
ProfesjaProgramista
  • Administrator
  • Szara eminencja
Avallach popraw mnie jeśli się mylę:
W czystym kodzie metoda odpowiada za określoną czynność np. kopiuj tekst, zapisz do pliku, dzięki czemu nazewnictwo jest proste i logiczne.
Tak.

MajkeI

MajkeI

Użytkownicy
Front End Developer
posty698
Propsy169
Profesjabrak
  • Użytkownicy
  • Front End Developer
Spoiler
using System;
using System.IO;

namespace Program {
    class Program {
        public static string GetDirectory() {
                Console.WriteLine("Input directory");
                return Console.ReadLine();
        }
        public static void ChangeFileNames (string directory) {
            string[] filePaths;
            if (!Directory.Exists(directory))
                Console.WriteLine("Directory is incorrect!");
            else {
                filePaths = Directory.GetFiles(directory);
                if (filePaths.Length == 0)
                    Console.WriteLine("No files in this directory!");
                else
                    Console.WriteLine("Changed files: ");
                for (int i = 0; i < filePaths.Length; i++) {
                    File.Move(filePaths[i], filePaths[i].ToUpper());
                    filePaths[i] = Path.GetFileName(filePaths[i]);
                    Console.WriteLine((i + 1) + ". " + filePaths[i]);
                }
            }
        }
        static void Main(string[] args) {
            while (true) {
                Console.Clear();
                ChangeFileNames(GetDirectory());
                Console.WriteLine("\n(Press ESCAPE to exit)");
                if (Console.ReadKey().Key == ConsoleKey.Escape)
                    return;
            }
        }
    }
}

Nie tworzyłem metody "Exit" bo musiałbym i tak stworzyć boola i sprawdzać go w Main(), a w samej metodzie ew sprawdzać sam klawisz, to byłoby raczej bez senusu. Podzieliłem tak - jest ok?
 

inż. Avallach

inż. Avallach

Administrator
posty7662
Propsy5238
NagrodyV
ProfesjaProgramista
  • Administrator

inż. Avallach
Administrator

[C#] Obsługa błędów I/O
#8 2015-05-27, 16:19(Ostatnia zmiana: 2015-05-27, 16:24)
Nie do końca. Sprawdzanie czy katalog istnieje i obsługa sytuacji kiedy nie istnieje zdecydowanie powinny być w metodzie GetDirectory, nie w metodzie ChangeFileNames. Pomyśl o tym tak: metoda GetDirectory "obiecuje" zwrócić katalog (ścieżkę do niego). Tymczasem u ciebie może zwrócić ścieżkę która do niczego nie prowadzi, może "kłamać"! Taki kod nie jest czysty.
Z kolei dlaczego metoda ChangeFileNames miałaby sprawdzać czy katalog istnieje? Przecież dostaje katalog na wejściu. To jej wymaganie wstępne  - jeśli ktoś ją wywołał podając błędną ścieżkę, to wyjątek będzie jego "winą", nie tej metody.

MajkeI

MajkeI

Użytkownicy
Front End Developer
posty698
Propsy169
Profesjabrak
  • Użytkownicy
  • Front End Developer
Tylko jak metoda GetDirectory ma jednocześnie zwracać katalog i true/false (czy istnieje czy nie) ?

Spoiler
        public static string GetDirectory()
        {
            Console.WriteLine("Input directory");
            string dir = Console.ReadLine();

            if (!Directory.Exists(dir))
                Console.WriteLine("Directory is incorrect!");
            else
                return dir;

            return "";           //?????
        }
 

Adanos

Adanos

Administrator
Szara eminencja
posty5204
Propsy3870
ProfesjaProgramista
  • Administrator
  • Szara eminencja
Cytuj
Tylko jak metoda GetDirectory ma jednocześnie zwracać katalog i true/false (czy istnieje czy nie) ?
Metoda GetDirectory ma zwracać tylko katalog, ewentualnie nulla lub pusty napis. Czy katalog istnieje lub nie, sprawdzasz za pomocą Directory.Exists(dir). Nie komplikuj sobie życia.

MajkeI

MajkeI

Użytkownicy
Front End Developer
posty698
Propsy169
Profesjabrak
  • Użytkownicy
  • Front End Developer
Tak, mam to w metodzie. Ale jeśli katalog jest nieprawidłowy to i tak przejdzie dalej, do ChangeFileNames() i wywali wtedy błąd
ChangeFileNames(GetDirectory());

Chyba, że tak?
using System;
using System.IO;

namespace Program
{
    class Program
    {
        public static void GetDirectory()
        {
            Console.WriteLine("Input directory");
            string dir = Console.ReadLine();

            if (!Directory.Exists(dir))
                Console.WriteLine("Directory is incorrect!");
            else
                ChangeFileNames(dir);
        }
        public static void ChangeFileNames(string directory)
        {
            string[] filePaths = Directory.GetFiles(directory);

            if (filePaths.Length == 0)
                Console.WriteLine("No files in this directory!");
            else
                Console.WriteLine("Changed files: ");

            for (int i = 0; i < filePaths.Length; i++)
            {
                File.Move(filePaths[i], filePaths[i].ToUpper());
                filePaths[i] = Path.GetFileName(filePaths[i]);
                Console.WriteLine((i + 1) + ". " + filePaths[i]);
            }

        }
        static void Main(string[] args)
        {
            while (true)
            {
                Console.Clear();

                GetDirectory();

                Console.WriteLine("\n(Press ESCAPE to exit)");
                if (Console.ReadKey().Key == ConsoleKey.Escape)
                    return;
            }
        }
    }
}

Ale to chyba też nie jest "czyste" skoro jedną metodę wywołuję w drugiej
 

inż. Avallach

inż. Avallach

Administrator
posty7662
Propsy5238
NagrodyV
ProfesjaProgramista
  • Administrator
Aaargh, nie rozumiesz. Zmień kod tak, żeby metoda GetDirectory zawsze zwracała katalog (i nie wykonywała nie związanych z tym czynności, takich jak zmiana nazwy czegokolwiek).

Chyba że nie chcesz żeby GetDirectory zawsze zwracało katalog, to wtedy zmień nazwe na TryGetDirectory i np zwracaj nulla i odpowiednio to obsługuj w metodzie która tą wywołue (to już zależy od twojego projektu).

MajkeI

MajkeI

Użytkownicy
Front End Developer
posty698
Propsy169
Profesjabrak
  • Użytkownicy
  • Front End Developer
        public static string GetDirectory()
        {
            Console.WriteLine("Input directory");
            return Console.ReadLine();
        }
Zwraca zawsze, bez względu na błąd
 


MajkeI

MajkeI

Użytkownicy
Front End Developer
posty698
Propsy169
Profesjabrak
  • Użytkownicy
  • Front End Developer
            Console.WriteLine("Input directory");
            string dir = Console.ReadLine();

            if (!Directory.Exists(dir))
                Console.WriteLine("Directory is incorrect!");
            else
                return dir;

            return ""; // no i tutaj mam tak zostawić?
 

inż. Avallach

inż. Avallach

Administrator
posty7662
Propsy5238
NagrodyV
ProfesjaProgramista
  • Administrator
Nieee, teraz zwróci albo katalog, albo pustego stringa. A ma zawsze zwracać katalog. Podpowiedziałem ci już że chodzi o pętlę.
public static string GetDirectory()
{
    string dir = null;
    while(!Directory.Exists(dir))
    {
        Console.WriteLine("Input directory");
        dir = Console.ReadLine();
    }
    return dir;
}
W ten sposób metoda zawsze zrobi to co mówi jej nazwa - zwróci katalog. Nastepne metody będą mogły po prostu użyć tego co zwróci bez sprawdzania czy to katalog, bo przecież obiecała że zwróci katalog.

MajkeI

MajkeI

Użytkownicy
Front End Developer
posty698
Propsy169
Profesjabrak
  • Użytkownicy
  • Front End Developer

MajkeI

[C#] Obsługa błędów I/O
#17 2015-05-27, 18:18(Ostatnia zmiana: 2015-05-28, 08:50)
Właśnie to napisałem ... :C tylko troche inaczej.

Ale wywala błąd cały projekt teraz
EDIT: Aaaa okej, już to widze

Jak odwołać się do katalogu powyżej?
C:\Cośtam\.. to katalog niżej, sprawdziłem, ale co jeśli chcę podkatalog wyżej o nieznanej mi nazwie?
C:\Cośtam\* oczywiście nie działa

Pobawię się, jak coś wyjdzie lub nie to dam znać. Dzięki @Avallach , @Adanos

UPDATE: using System;
using System.IO;

namespace Program {
    class Program {
        public static string GetDirectory() {
            string dir = null;

            while (!Directory.Exists(dir)) {
                Console.Clear();
                Console.WriteLine("Input directory");
                dir = Console.ReadLine();
            }
            return dir;
        }
        public static void ChangeNamesWithSubfolders(string dir) {
            string[] filePaths = Directory.GetFiles(dir);

            if (filePaths.Length == 0)
{
Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine(" No files in " + dir + "!");
}
            else
{
Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine(" Changed files in " + dir + ":");
}

            for (int i = 0; i < filePaths.Length; i++) {
                File.Move(filePaths[i], filePaths[i].ToUpper());
                filePaths[i] = Path.GetFileName(filePaths[i]);
                Console.WriteLine("  - " + filePaths[i]);
            }

            DirectoryInfo directory = new DirectoryInfo(dir);
            DirectoryInfo[] directories = directory.GetDirectories();

            for (int i = 0; i < directories.Length; i++)
ChangeNamesWithSubfolders(dir + "\\" + directories[i]);

Console.ForegroundColor = ConsoleColor.Gray;
        }
        static void Main(string[] args) {
            while (true) {
                Console.Clear();

                ChangeNamesWithSubfolders(GetDirectory());

                Console.WriteLine("\n(Press ESCAPE to exit)");
                if (Console.ReadKey().Key == ConsoleKey.Escape)
                    return;
            }
        }
    }
}
Czy ten kod jest czysty?
 

Adanos

Adanos

Administrator
Szara eminencja
posty5204
Propsy3870
ProfesjaProgramista
  • Administrator
  • Szara eminencja
Akurat metodę ChangeNamesWithSubfolders możesz podzielić na 3 mniejsze:
- jakaś metoda "info":
if (filePaths.Length == 0)
{
Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine(" No files in " + dir + "!");
}
            else
{
Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine(" Changed files in " + dir + ":");
}
- zmiana nazwy:
for (int i = 0; i < filePaths.Length; i++) {
                File.Move(filePaths[i], filePaths[i].ToUpper());
                filePaths[i] = Path.GetFileName(filePaths[i]);
                Console.WriteLine("  - " + filePaths[i]);
            }
- sprawdzanie podkatalogów:
DirectoryInfo directory = new DirectoryInfo(dir);
            DirectoryInfo[] directories = directory.GetDirectories();

            for (int i = 0; i < directories.Length; i++)
ChangeNamesWithSubfolders(dir + "\\" + directories[i]);

Console.ForegroundColor = ConsoleColor.Gray;

Avallach już pisał to, ale nie używaj tego:
while (true)Zadeklaruj jakąś zmienną.

MajkeI

MajkeI

Użytkownicy
Front End Developer
posty698
Propsy169
Profesjabrak
  • Użytkownicy
  • Front End Developer
using System;
using System.IO;

namespace Program
{
    class Program
    {
        public static string GetDirectory()
        {
            string dir = null;
            while (!Directory.Exists(dir))
            {
                Console.Clear();
                Console.WriteLine("Input directory");
                dir = Console.ReadLine();
            }
            return dir;
        }
        public static void GetInfoAboutDirectory(string[] filePaths, string dir)
        {
            if (filePaths.Length == 0)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine(" No files in " + dir + "!");
            }
            else
            {
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine(" Changed files in " + dir + ":");
            }
        }
        public static void ChangeFileNames(string[] filePaths)
        {
            for (int i = 0; i < filePaths.Length; i++)
            {
                File.Move(filePaths[i], filePaths[i].ToUpper());
                filePaths[i] = Path.GetFileName(filePaths[i]);
                Console.WriteLine("  - " + filePaths[i]);
            }
        }
        public static void CheckSubfolders(string dir)
        {
            DirectoryInfo directory = new DirectoryInfo(dir);
            DirectoryInfo[] directories = directory.GetDirectories();
            for (int i = 0; i < directories.Length; i++)
                ChangeNamesWithSubfolders(dir + "\\" + directories[i]);
        }
        public static void ChangeNamesWithSubfolders(string dir)
        {
            string[] filePaths = Directory.GetFiles(dir);
            GetInfoAboutDirectory(filePaths, dir);
            ChangeFileNames(filePaths);
            CheckSubfolders(dir);
            Console.ForegroundColor = ConsoleColor.Gray;
        }
        static void Main(string[] args)
        {
            bool end = false;
            while (end == false)
            {
                Console.Clear();
                ChangeNamesWithSubfolders(GetDirectory());
                Console.WriteLine("\n(Press ESCAPE to exit)");
                if (Console.ReadKey().Key == ConsoleKey.Escape)
                    end = true;
            }
        }
    }
}

?
 


0 użytkowników i 1 Gość przegląda ten wątek.
0 użytkowników
Do góry