Sprawdź, czy ciąg jest prawidłową ścieżką do katalogu (folderu) systemu Windows

84

Próbuję ustalić, czy ciąg wprowadzony przez użytkownika jest prawidłowy do reprezentowania ścieżki do folderu. Przez prawidłowy mam na myśli odpowiednio sformatowany.

W mojej aplikacji folder reprezentuje miejsce docelowe instalacji. Pod warunkiem, że ścieżka do folderu jest prawidłowa, chcę ustalić, czy folder istnieje i utworzyć go, jeśli nie.

Obecnie używam IO.Directory.Exists( String path ). Uważam, że działa to dobrze, z wyjątkiem sytuacji, gdy użytkownik nie sformatował poprawnie ciągu. W takim przypadku ta metoda zwróci wartość false, co oznacza, że ​​folder nie istnieje. Ale to jest problem, ponieważ później nie będę mógł utworzyć folderu.

Z mojego googlowania znalazłem sugestię, aby użyć wyrażenia regularnego, aby sprawdzić, czy format jest prawidłowy. Nie mam doświadczenia z wyrażeniami regularnymi i zastanawiam się, czy jest to wykonalne podejście. Oto co znalazłem:

Regex r = new Regex( @"^(([a-zA-Z]\:)|(\\))(\\{1}|((\\{1})[^\\]([^/:*?<>""|]*))+)$" );
return r.IsMatch( path );

Czy test wyrażenia regularnego w połączeniu z Directory.Exists(), dałby mi wystarczająco dobrą metodę sprawdzenia, czy ścieżka jest prawidłowa i czy istnieje? Wiem, że będzie się to różnić w zależności od systemu operacyjnego i innych czynników, ale program jest przeznaczony tylko dla użytkowników systemu Windows .

Pudpuduk
źródło
1
Jeśli nie utworzy katalogu po tym, jak Directory.Exists zwróci wartość false, czy nie jest to całkiem dobra wskazówka, że ​​użytkownik podał złe dane wejściowe?
Robert Harvey
2
@Robert Widziałem to pytanie i nie udzieliło konkretnej odpowiedzi poza ogólnymi zasadami. Druga najwyższa odpowiedź nie dotyczyła formatowania, a jedynie nieprawidłowe znaki. Również metoda Directory.Exists może zwrócić wartość false, ale ponieważ chcę mieć możliwość tworzenia folderu na miejscu, nie mogę tak po prostu przejść.
Pudpuduk
@Robert W drugim temacie, z którym się połączyłeś - wpisanie jednego słowa i tak mogłoby przejść walidację podaną w odpowiedziach na to pytanie.
Pudpuduk

Odpowiedzi:

116

Zadzwoń Path.GetFullPath; wyrzuci wyjątki, jeśli ścieżka jest nieprawidłowa.

Aby nie zezwalać na ścieżki względne (takie jak Word), wywołaj Path.IsPathRooted.

SLaks
źródło
Wiedziałem, że jest coś prostszego! I dzięki, nie myślałem o problemie względności ścieżek.
Pudpuduk
2
Dzięki, SLaks. Widziałem wiele duplikatów i przeprowadziłem wiele wyszukiwań w Google (więcej niż jeden raz), ale po raz pierwszy widziałem dobrą odpowiedź na to konkretne pytanie.
Robert Harvey
5
Path.GetFullPath („con.txt”) to poprawna nazwa pliku.
Christoffer
8
@Slaks To jest zbyt stare, aby zostawić komentarz, ale nadal chcę zostawić tutaj jeden z powodu, dla którego przyznałem ci mój głos -1. Wydaje się, że Path.GetFullPath () działa poprawnie, ale co, jeśli ścieżka brzmi: „Z: \\\\\\\\ Hi \\\\\\\ Tam”, to nie jest prawidłowa ścieżka bezwzględna, ale Path.GetFullPath (...) podaje wynik: Z: \ Hi \ Tam i nie ma zgłoszonego wyjątku. Musiałem to trochę zmienić, porównując ciąg zwrócony przez GetFullPath () i oryginalny ciąg w następujący sposób: private bool IsPathValid (ścieżka ciągu) {try {string fullPath = Path.GetFullPath (path); return fullPath == ścieżka; } catch {return false;}}
King King
4
@KingKing Z tej odpowiedzi Linuksa na unix.stackexchange.com: „Wiele ukośników jest dozwolonych i jest odpowiednikiem jednego ukośnika ..” Zaobserwowałem to samo w systemie Windows (chociaż początkowe ukośniki w ścieżce UNC mogą być traktowane inaczej). Na dowód tego, w wierszu polecenia, spróbuj tego: cd C:\\\\\\\Windows\\\\\\\System32. W przypadku systemu Windows nie mogę znaleźć autorytatywnego źródła, które dokumentuje to zachowanie, ale oczywiście mile widziany jest wskaźnik do takiego.
DavidRR
20

Właściwie nie zgadzam się ze SLaksami. To rozwiązanie nie zadziałało. Wyjątek nie wydarzył się zgodnie z oczekiwaniami. Ale ten kod zadziałał dla mnie:

if(System.IO.Directory.Exists(path))
{
    ...
}
Scott Shaw-Smith
źródło
62
Prawidłowa ścieżka niekoniecznie jest katalogiem, który istnieje ... i właśnie o to tutaj chodzi
Benlitz,
1
Pytanie dotyczyło walidacji ciągu ścieżki, ścieżki, która może nie istnieć.
Mubashar
Myślę, że ten sposób jest poprawny. Nie należy się spodziewać wyjątków. Ta metoda sprawdza również nieprawidłowe znaki w podanej ścieżce.
Eugene Maksimov
Ten warunek sam zgłosi wyjątek, jeśli „ścieżka” nie jest prawdziwą ścieżką, ponieważ Directory.Exists wymaga prawidłowej ścieżki.
M. Fawad Surosh
Zupełnie zła odpowiedź! Zastanawiam się, jak otrzymał 32 głosy poparcia (na razie). Muszą być przez ludzi, którzy szukali niewłaściwego miejsca dla problemu, z którym się borykali, i natknęli się na to.
Sнаđошƒаӽ
13

Path.GetFullPath podaje tylko poniższe wyjątki

Ścieżka ArgumentException jest ciągiem o zerowej długości, zawiera tylko biały znak lub zawiera co najmniej jeden nieprawidłowy znak zdefiniowany w GetInvalidPathChars. -lub- System nie mógł pobrać ścieżki bezwzględnej.

SecurityException Obiekt wywołujący nie ma wymaganych uprawnień.

Ścieżka ArgumentNullException ma wartość null.

Ścieżka NotSupportedException zawiera dwukropek („:”), który nie jest częścią identyfikatora woluminu (na przykład „c: \”).

PathTooLongException Określona ścieżka, nazwa pliku lub oba te elementy przekraczają maksymalną długość zdefiniowaną przez system. Na przykład na platformach Windows ścieżki muszą mieć mniej niż 248 znaków, a nazwy plików muszą mieć mniej niż 260 znaków.

Alternatywnym sposobem jest użycie:

/// <summary>
/// Validate the Path. If path is relative append the path to the project directory by default.
/// </summary>
/// <param name="path">Path to validate</param>
/// <param name="RelativePath">Relative path</param>
/// <param name="Extension">If want to check for File Path</param>
/// <returns></returns>
private static bool ValidateDllPath(ref string path, string RelativePath = "", string Extension = "")
{
    // Check if it contains any Invalid Characters.
    if (path.IndexOfAny(Path.GetInvalidPathChars()) == -1)
    {
        try
        {
            // If path is relative take %IGXLROOT% as the base directory
            if (!Path.IsPathRooted(path))
            {
                if (string.IsNullOrEmpty(RelativePath))
                {
                    // Exceptions handled by Path.GetFullPath
                    // ArgumentException path is a zero-length string, contains only white space, or contains one or more of the invalid characters defined in GetInvalidPathChars. -or- The system could not retrieve the absolute path.
                    // 
                    // SecurityException The caller does not have the required permissions.
                    // 
                    // ArgumentNullException path is null.
                    // 
                    // NotSupportedException path contains a colon (":") that is not part of a volume identifier (for example, "c:\"). 
                    // PathTooLongException The specified path, file name, or both exceed the system-defined maximum length. For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters.

                    // RelativePath is not passed so we would take the project path 
                    path = Path.GetFullPath(RelativePath);

                }
                else
                {
                    // Make sure the path is relative to the RelativePath and not our project directory
                    path = Path.Combine(RelativePath, path);
                }
            }

            // Exceptions from FileInfo Constructor:
            //   System.ArgumentNullException:
            //     fileName is null.
            //
            //   System.Security.SecurityException:
            //     The caller does not have the required permission.
            //
            //   System.ArgumentException:
            //     The file name is empty, contains only white spaces, or contains invalid characters.
            //
            //   System.IO.PathTooLongException:
            //     The specified path, file name, or both exceed the system-defined maximum
            //     length. For example, on Windows-based platforms, paths must be less than
            //     248 characters, and file names must be less than 260 characters.
            //
            //   System.NotSupportedException:
            //     fileName contains a colon (:) in the middle of the string.
            FileInfo fileInfo = new FileInfo(path);

            // Exceptions using FileInfo.Length:
            //   System.IO.IOException:
            //     System.IO.FileSystemInfo.Refresh() cannot update the state of the file or
            //     directory.
            //
            //   System.IO.FileNotFoundException:
            //     The file does not exist.-or- The Length property is called for a directory.
            bool throwEx = fileInfo.Length == -1;

            // Exceptions using FileInfo.IsReadOnly:
            //   System.UnauthorizedAccessException:
            //     Access to fileName is denied.
            //     The file described by the current System.IO.FileInfo object is read-only.-or-
            //     This operation is not supported on the current platform.-or- The caller does
            //     not have the required permission.
            throwEx = fileInfo.IsReadOnly;

            if (!string.IsNullOrEmpty(Extension))
            {
                // Validate the Extension of the file.
                if (Path.GetExtension(path).Equals(Extension, StringComparison.InvariantCultureIgnoreCase))
                {
                    // Trim the Library Path
                    path = path.Trim();
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else
            {
                return true;

            }
        }
        catch (ArgumentNullException)
        {
            //   System.ArgumentNullException:
            //     fileName is null.
        }
        catch (System.Security.SecurityException)
        {
            //   System.Security.SecurityException:
            //     The caller does not have the required permission.
        }
        catch (ArgumentException)
        {
            //   System.ArgumentException:
            //     The file name is empty, contains only white spaces, or contains invalid characters.
        }
        catch (UnauthorizedAccessException)
        {
            //   System.UnauthorizedAccessException:
            //     Access to fileName is denied.
        }
        catch (PathTooLongException)
        {
            //   System.IO.PathTooLongException:
            //     The specified path, file name, or both exceed the system-defined maximum
            //     length. For example, on Windows-based platforms, paths must be less than
            //     248 characters, and file names must be less than 260 characters.
        }
        catch (NotSupportedException)
        {
            //   System.NotSupportedException:
            //     fileName contains a colon (:) in the middle of the string.
        }
        catch (FileNotFoundException)
        {
            // System.FileNotFoundException
            //  The exception that is thrown when an attempt to access a file that does not
            //  exist on disk fails.
        }
        catch (IOException)
        {
            //   System.IO.IOException:
            //     An I/O error occurred while opening the file.
        }
        catch (Exception)
        {
            // Unknown Exception. Might be due to wrong case or nulll checks.
        }
    }
    else
    {
        // Path contains invalid characters
    }
    return false;
}
vCillusion
źródło
9

Oto rozwiązanie, które wykorzystuje Path.GetFullPath zgodnie z zaleceniami w odpowiedzi @SLaks .

W kodzie, który tu zamieszczam, zwróć uwagę, że IsValidPath(string path)jest on zaprojektowany w taki sposób, że wywołujący nie musi martwić się obsługą wyjątków .

Może się również okazać, że metoda, którą wywołuje, TryGetFullPath(...)również sama w sobie ma wartość, gdy chcesz bezpiecznie próbować uzyskać absolutną ścieżkę .

/// <summary>
/// Gets a value that indicates whether <paramref name="path"/>
/// is a valid path.
/// </summary>
/// <returns>Returns <c>true</c> if <paramref name="path"/> is a
/// valid path; <c>false</c> otherwise. Also returns <c>false</c> if
/// the caller does not have the required permissions to access
/// <paramref name="path"/>.
/// </returns>
/// <seealso cref="Path.GetFullPath"/>
/// <seealso cref="TryGetFullPath"/>
public static bool IsValidPath(string path)
{
    string result;
    return TryGetFullPath(path, out result);
}

/// <summary>
/// Returns the absolute path for the specified path string. A return
/// value indicates whether the conversion succeeded.
/// </summary>
/// <param name="path">The file or directory for which to obtain absolute
/// path information.
/// </param>
/// <param name="result">When this method returns, contains the absolute
/// path representation of <paramref name="path"/>, if the conversion
/// succeeded, or <see cref="String.Empty"/> if the conversion failed.
/// The conversion fails if <paramref name="path"/> is null or
/// <see cref="String.Empty"/>, or is not of the correct format. This
/// parameter is passed uninitialized; any value originally supplied
/// in <paramref name="result"/> will be overwritten.
/// </param>
/// <returns><c>true</c> if <paramref name="path"/> was converted
/// to an absolute path successfully; otherwise, false.
/// </returns>
/// <seealso cref="Path.GetFullPath"/>
/// <seealso cref="IsValidPath"/>
public static bool TryGetFullPath(string path, out string result)
{
    result = String.Empty;
    if (String.IsNullOrWhiteSpace(path)) { return false; }
    bool status = false;

    try
    {
        result = Path.GetFullPath(path);
        status = true;
    }
    catch (ArgumentException) { }
    catch (SecurityException) { }
    catch (NotSupportedException) { }
    catch (PathTooLongException) { }

    return status;
}
DavidRR
źródło
6

Użyj tego kodu

string DirectoryName = "Sample Name For Directory Or File";
Path.GetInvalidFileNameChars()
  .Where(x => DirectoryName.Contains(x))
  .Count() > 0 || DirectoryName == "con"
user1508188
źródło
4
Nieco krótszy kod realizujący to samo: Path.GetInvalidFileNameChars().Any(DirectoryName.Contains) || DirectoryName == "con"
bsegraves
2
@nawfal Indeed. Z nazewnictwa plików, ścieżek i przestrzeni nazw w witrynie MSDN: „Nie używaj następujących zastrzeżonych nazw dla nazwy pliku: CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, ​​COM5, COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8 i LPT9. Unikaj także tych nazw, po których bezpośrednio następuje rozszerzenie; na przykład NUL.txt nie jest zalecany. Aby uzyskać więcej informacji, zobacz Przestrzenie nazw . "
DavidRR
To „podejście do czarnej listy” nie działa w każdym systemie Windows, na przykład gdy pojawiają się piktogramy: en.wikipedia.org/wiki/Miscellaneous_Symbols_and_Pictographs
stycznia
4
    private bool IsValidPath(string path)
    {
        Regex driveCheck = new Regex(@"^[a-zA-Z]:\\$");
        if (!driveCheck.IsMatch(path.Substring(0, 3))) return false;
        string strTheseAreInvalidFileNameChars = new string(Path.GetInvalidPathChars());
        strTheseAreInvalidFileNameChars += @":/?*" + "\"";
        Regex containsABadCharacter = new Regex("[" + Regex.Escape(strTheseAreInvalidFileNameChars) + "]");
        if (containsABadCharacter.IsMatch(path.Substring(3, path.Length - 3)))
            return false;

        DirectoryInfo dir = new DirectoryInfo(Path.GetFullPath(path));
        if (!dir.Exists)
            dir.Create();
        return true;
    }
Alex Jolig
źródło
3

Nie miałem żadnych problemów z tym kodem:

private bool IsValidPath(string path, bool exactPath = true)
{
    bool isValid = true;

    try
    {
        string fullPath = Path.GetFullPath(path);

        if (exactPath)
        {
            string root = Path.GetPathRoot(path);
            isValid = string.IsNullOrEmpty(root.Trim(new char[] { '\\', '/' })) == false;
        }
        else
        {
            isValid = Path.IsPathRooted(path);
        }
    }
    catch(Exception ex)
    {
        isValid = false;
    }

    return isValid;
}

Na przykład te zwróciłyby fałsz:

IsValidPath("C:/abc*d");
IsValidPath("C:/abc?d");
IsValidPath("C:/abc\"d");
IsValidPath("C:/abc<d");
IsValidPath("C:/abc>d");
IsValidPath("C:/abc|d");
IsValidPath("C:/abc:d");
IsValidPath("");
IsValidPath("./abc");
IsValidPath("/abc");
IsValidPath("abc");
IsValidPath("abc", false);

A te zwróciłyby prawdę:

IsValidPath(@"C:\\abc");
IsValidPath(@"F:\FILES\");
IsValidPath(@"C:\\abc.docx\\defg.docx");
IsValidPath(@"C:/abc/defg");
IsValidPath(@"C:\\\//\/\\/\\\/abc/\/\/\/\///\\\//\defg");
IsValidPath(@"C:/abc/def~`!@#$%^&()_-+={[}];',.g");
IsValidPath(@"C:\\\\\abc////////defg");
IsValidPath(@"/abc", false);
Poszukiwacz Dao
źródło
0

Prostsze rozwiązanie niezależne od systemu operacyjnego.

public static class PathHelper
{
    public static void ValidatePath(string path)
    {
        if (!Directory.Exists(path))
            Directory.CreateDirectory(path).Delete();
    }
}

Stosowanie:

try
{
    PathHelper.ValidatePath(path);
}
catch(Exception e)
{
    // handle exception
}

Directory.CreateDirectory() automatycznie wyrzuci we wszystkich następujących sytuacjach:

System.IO.IOException:
katalog określony przez ścieżkę jest plikiem. -lub- Nazwa sieci nie jest znana.

System.UnauthorizedAccessException:
wywołujący nie ma wymaganych uprawnień.

System.ArgumentException:
path to ciąg o zerowej długości, zawierający tylko odstępy lub co najmniej jeden nieprawidłowy znak. Możesz zapytać o nieprawidłowe znaki przy użyciu metody System.IO.Path.GetInvalidPathChars. -lub- ścieżka jest poprzedzona przedrostkiem lub zawiera tylko znak dwukropka (:).

System.ArgumentNullException:
ścieżka ma wartość null.

System.IO.PathTooLongException:
określona ścieżka, nazwa pliku lub oba te elementy przekraczają maksymalną długość zdefiniowaną przez system.

System.IO.DirectoryNotFoundException:
określona ścieżka jest nieprawidłowa (na przykład znajduje się na niezamapowanym dysku).

System.NotSupportedException:
ścieżka zawiera znak dwukropka (:), który nie jest częścią etykiety dysku („C:”).

Szkopuł
źródło