Jak sprawdzić, czy podany ciąg jest prawidłową / prawidłową nazwą pliku w systemie Windows?

165

Chcę dołączyć funkcję zmiany nazwy pliku wsadowego do mojej aplikacji. Użytkownik może wpisać wzorzec docelowej nazwy pliku i (po zastąpieniu niektórych symboli wieloznacznych we wzorcu) muszę sprawdzić, czy będzie to poprawna nazwa pliku w systemie Windows. Próbowałem użyć wyrażenia regularnego, takiego jak, [a-zA-Z0-9_]+ale nie zawiera ono wielu znaków narodowych z różnych języków (np. Umlauty i tak dalej). Jaki jest najlepszy sposób wykonania takiej kontroli?

tłuc
źródło
Sugeruję użycie statycznie skompilowanego Regex, jeśli masz zamiar użyć którejkolwiek odpowiedzi z Regex ..
AMissico

Odpowiedzi:

100

Możesz pobrać listę nieprawidłowych znaków z Path.GetInvalidPathCharsi GetInvalidFileNameChars.

UPD: Zobacz sugestię Steve'a Coopera, jak używać ich w wyrażeniu regularnym.

UPD2: Należy zauważyć, że zgodnie z sekcją Uwagi w witrynie MSDN „Nie gwarantuje się, że tablica zwrócona przez tę metodę będzie zawierała pełny zestaw znaków, które są nieprawidłowe w nazwach plików i katalogów”. Odpowiedź udzielona przez sixlettervaliables zawiera więcej szczegółów.

Eugene Katz
źródło
11
To nie odpowiada na pytanie; istnieje wiele ciągów składających się wyłącznie z prawidłowych znaków (np. „....”, „CON”, ciągi znaków o długości setek znaków), które nie są prawidłowymi nazwami plików.
Dour High Arch,
31
Czy ktoś jeszcze rozczarował, że MS nie zapewnia funkcji / API na poziomie systemu dla tej możliwości, a każdy programista musi ugotować własne rozwiązanie? Zastanawiasz się, czy istnieje bardzo dobry powód, czy tylko niedopatrzenie ze strony SM.
Thomas Nguyen,
@High Arch: Zobacz odpowiedź na pytanie „W języku C # sprawdź, czy nazwa pliku jest prawdopodobnie prawidłowa (nie oznacza, że ​​istnieje)”. (Chociaż niektórzy sprytni faceci zamknęli to pytanie na korzyść tego ...)
mmmmmmmm
129

Z artykułu „Nazywanie pliku lub katalogu” w witrynie MSDN można zapoznać się z ogólnymi konwencjami określającymi prawidłową nazwę pliku w systemie Windows:

Możesz użyć dowolnego znaku w bieżącej stronie kodowej (Unicode / ANSI powyżej 127), z wyjątkiem:

  • < > : " / \ | ? *
  • Znaki, których reprezentacje liczb całkowitych mieszczą się w zakresie 0-31 (mniej niż spacja ASCII)
  • Każdy inny znak, na który docelowy system plików nie zezwala (np. Końcowe kropki lub spacje)
  • Dowolna z nazw DOS: CON, PRN, AUX, NUL, COM0, COM1, COM2, COM3, COM4, ​​COM5, COM6, COM7, COM8, COM9, LPT0, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9 (unikaj plików AUX.txt itp.)
  • Nazwa pliku to wszystkie kropki

Kilka opcjonalnych rzeczy do sprawdzenia:

  • Ścieżki plików (w tym nazwa pliku) nie mogą mieć więcej niż 260 znaków (bez \?\prefiksu)
  • Ścieżki plików Unicode (w tym nazwa pliku) zawierające ponad 32 000 znaków \?\(należy pamiętać, że prefiks może rozszerzyć składniki katalogu i spowodować przekroczenie limitu 32 000)
user7116
źródło
8
+1 za dołączenie zarezerwowanych nazw plików - tych brakowało w poprzednich odpowiedziach.
SqlRyan
2
„AUX” jest doskonale użyteczną nazwą pliku, jeśli używasz składni „\\? \”. Oczywiście programy, które nie używają tej składni, mają z nią poważne problemy ... (Testowane na XP)
user9876
9
Prawidłowe wyrażenie regularne dla wszystkich tych warunków wymienionych powyżej jest następujące:Regex unspupportedRegex = new Regex("(^(PRN|AUX|NUL|CON|COM[1-9]|LPT[1-9]|(\\.+)$)(\\..*)?$)|(([\\x00-\\x1f\\\\?*:\";|/<>])+)|(([\\. ]+)", RegexOptions.IgnoreCase);
dlaczego, dlaczego,
4
@whywhywhy Myślę, że masz dodatkowy nawias otwierający w tym Regexie. "(^ (PRN | AUX | NUL | CON | COM [1-9] | LPT [1-9] | (\\. +) $) (\\ .. *)? $) | (([\\ x00 - \\ x1f \\\\? *: \ "; ‌ | / <>]) +) | ([\\.] +)" pracował dla mnie.
Wilky
4
Przeczytałem ten sam artykuł, o którym mowa w tej odpowiedzi, i po eksperymentach odkryłem, że COM0 i LPT0 również nie są dozwolone. @dlf ten działa z nazwami plików zaczynającymi się od „.”:^(?!^(?:PRN|AUX|CLOCK\$|NUL|CON|COM\d|LPT\d)(?:\..+)?$)(?:\.*?(?!\.))[^\x00-\x1f\\?*:\";|\/<>]+(?<![\s.])$
mjohnsonengr
67

W przypadku .Net Frameworks starszych niż 3.5 powinno to działać:

Dopasowywanie wyrażeń regularnych powinno ci pomóc. Oto fragment używający System.IO.Path.InvalidPathCharsstałej;

bool IsValidFilename(string testName)
{
    Regex containsABadCharacter = new Regex("[" 
          + Regex.Escape(System.IO.Path.InvalidPathChars) + "]");
    if (containsABadCharacter.IsMatch(testName)) { return false; };

    // other checks for UNC, drive-path format, etc

    return true;
}

W przypadku .Net Frameworks po wersji 3.0 powinno to działać:

http://msdn.microsoft.com/en-us/library/system.io.path.getinvalidpathchars(v=vs.90).aspx

Dopasowywanie wyrażeń regularnych powinno ci pomóc. Oto fragment używający System.IO.Path.GetInvalidPathChars()stałej;

bool IsValidFilename(string testName)
{
    Regex containsABadCharacter = new Regex("["
          + Regex.Escape(new string(System.IO.Path.GetInvalidPathChars())) + "]");
    if (containsABadCharacter.IsMatch(testName)) { return false; };

    // other checks for UNC, drive-path format, etc

    return true;
}

Gdy już to wiesz, powinieneś również sprawdzić różne formaty, np. c:\my\driveI\\server\share\dir\file.ext

Steve Cooper
źródło
czy to nie sprawdza tylko ścieżki, a nie nazwy pliku?
Eugene Katz
30
string strTheseAreInvalidFileNameChars = nowy ciąg (System.IO.Path.GetInvalidFileNameChars ()); Regex regFixFileName = new Regex ("[" + Regex.Escape (strTheseAreInvalidFileNameChars) + "]");
rao
2
Trochę badań od ludzi zdziałaby cuda. Zaktualizowałem post, aby odzwierciedlić zmiany.
Erik Philips
1
Drugi fragment kodu nie kompiluje się. „Nie można przekonwertować znaku [] na łańcuch
Paul Hunt
1
@AshkanMobayenKhiabani: InvalidPathChars jest przestarzały, ale GetInvalidPathChars nie.
IvanH
25

Spróbuj go użyć i wyłapuj błąd. Dozwolony zestaw może się zmieniać w różnych systemach plików lub w różnych wersjach systemu Windows. Innymi słowy, jeśli chcesz wiedzieć, czy Windows lubi tę nazwę, podaj jej nazwę i pozwól jej powiedzieć.


źródło
1
Wydaje się, że jest to jedyny test sprawdzający pod kątem wszystkich ograniczeń. Dlaczego inne odpowiedzi są wybierane zamiast tego?
przerwa
5
@gap, ponieważ nie zawsze działa. Na przykład próba uzyskania dostępu do CON często kończy się sukcesem, nawet jeśli nie jest to prawdziwy plik.
Antymon
4
Jednak zawsze lepiej unikać narzutów pamięci związanych z rzucaniem wyjątków, jeśli to możliwe.
Owen Blacker
2
Możesz też nie mieć uprawnień dostępu do niego; np. przetestować go pisząc, nawet jeśli możesz go przeczytać, jeśli tak jest lub będzie istnieć.
CodeLurker,
23

Ta klasa czyści nazwy plików i ścieżki; użyj go jak

var myCleanPath = PathSanitizer.SanitizeFilename(myBadPath, ' ');

Oto kod;

/// <summary>
/// Cleans paths of invalid characters.
/// </summary>
public static class PathSanitizer
{
    /// <summary>
    /// The set of invalid filename characters, kept sorted for fast binary search
    /// </summary>
    private readonly static char[] invalidFilenameChars;
    /// <summary>
    /// The set of invalid path characters, kept sorted for fast binary search
    /// </summary>
    private readonly static char[] invalidPathChars;

    static PathSanitizer()
    {
        // set up the two arrays -- sorted once for speed.
        invalidFilenameChars = System.IO.Path.GetInvalidFileNameChars();
        invalidPathChars = System.IO.Path.GetInvalidPathChars();
        Array.Sort(invalidFilenameChars);
        Array.Sort(invalidPathChars);

    }

    /// <summary>
    /// Cleans a filename of invalid characters
    /// </summary>
    /// <param name="input">the string to clean</param>
    /// <param name="errorChar">the character which replaces bad characters</param>
    /// <returns></returns>
    public static string SanitizeFilename(string input, char errorChar)
    {
        return Sanitize(input, invalidFilenameChars, errorChar);
    }

    /// <summary>
    /// Cleans a path of invalid characters
    /// </summary>
    /// <param name="input">the string to clean</param>
    /// <param name="errorChar">the character which replaces bad characters</param>
    /// <returns></returns>
    public static string SanitizePath(string input, char errorChar)
    {
        return Sanitize(input, invalidPathChars, errorChar);
    }

    /// <summary>
    /// Cleans a string of invalid characters.
    /// </summary>
    /// <param name="input"></param>
    /// <param name="invalidChars"></param>
    /// <param name="errorChar"></param>
    /// <returns></returns>
    private static string Sanitize(string input, char[] invalidChars, char errorChar)
    {
        // null always sanitizes to null
        if (input == null) { return null; }
        StringBuilder result = new StringBuilder();
        foreach (var characterToTest in input)
        {
            // we binary search for the character in the invalid set. This should be lightning fast.
            if (Array.BinarySearch(invalidChars, characterToTest) >= 0)
            {
                // we found the character in the array of 
                result.Append(errorChar);
            }
            else
            {
                // the character was not found in invalid, so it is valid.
                result.Append(characterToTest);
            }
        }

        // we're done.
        return result.ToString();
    }

}
Steve Cooper
źródło
1
Twoja odpowiedź mogłaby być lepiej dopasowana tutaj: stackoverflow.com/questions/146134/ ...
nawfal
22

Oto czego używam:

    public static bool IsValidFileName(this string expression, bool platformIndependent)
    {
        string sPattern = @"^(?!^(PRN|AUX|CLOCK\$|NUL|CON|COM\d|LPT\d|\..*)(\..+)?$)[^\x00-\x1f\\?*:\"";|/]+$";
        if (platformIndependent)
        {
           sPattern = @"^(([a-zA-Z]:|\\)\\)?(((\.)|(\.\.)|([^\\/:\*\?""\|<>\. ](([^\\/:\*\?""\|<>\. ])|([^\\/:\*\?""\|<>]*[^\\/:\*\?""\|<>\. ]))?))\\)*[^\\/:\*\?""\|<>\. ](([^\\/:\*\?""\|<>\. ])|([^\\/:\*\?""\|<>]*[^\\/:\*\?""\|<>\. ]))?$";
        }
        return (Regex.IsMatch(expression, sPattern, RegexOptions.CultureInvariant));
    }

Pierwszy wzorzec tworzy wyrażenie regularne zawierające nieprawidłowe / niedozwolone nazwy plików i znaki tylko dla platform Windows. Drugi robi to samo, ale zapewnia, że ​​nazwa jest legalna dla każdej platformy.

Scott Dorman
źródło
4
sPattern regex nie zezwala na pliki zaczynające się od znaku kropki. Jednak MSDN mówi, że „dopuszczalne jest określenie kropki jako pierwszego znaku nazwy. Na przykład„ .temp ””.
Usunąłbym
(Stopniowo ulepszyłem to i usunąłem poprzednie komentarze, które zostawiłem) Ten jest lepszy niż wyrażenie regularne odpowiedzi, ponieważ dopuszcza „.gitignore”, „..asdf”, nie zezwala na „<” i „>” lub jen znak i nie dopuszcza spacji ani kropki na końcu (co nie zezwala na nazwy składające się wyłącznie z kropek):@"^(?!(?:PRN|AUX|CLOCK\$|NUL|CON|COM\d|LPT\d)(?:\..+)?$)[^\x00-\x1F\xA5\\?*:\"";|\/<>]+(?<![\s.])$"
mjohnsonengr
to kończy się niepowodzeniem dla wszystkich testowanych plików. uruchomienie go dla C: \ Windows \ System32 \ msxml6.dll zgłasza fałsz.
magicandre1981
@ magicandre1981 Musisz podać tylko nazwę pliku, a nie pełną ścieżkę.
Scott Dorman
ok, ale muszę sprawdzić, czy pełna ścieżka jest prawidłowa. Użyłem teraz innego rozwiązania.
magicandre1981
18

Jeden narożny przypadek, o którym należy pamiętać, który mnie zaskoczył, gdy się o tym dowiedziałem: Windows pozwala na wprowadzanie spacji w nazwach plików! Na przykład wszystkie poniższe są poprawnymi i różnymi nazwami plików w systemie Windows (bez cudzysłowów):

"file.txt"
" file.txt"
"  file.txt"

Jeden wniosek z tego: zachowaj ostrożność podczas pisania kodu, który odcina początkowe / końcowe białe znaki z ciągu znaków nazwy pliku.

Jon Schneider
źródło
10

Upraszczając odpowiedź Eugene'a Katza:

bool IsFileNameCorrect(string fileName){
    return !fileName.Any(f=>Path.GetInvalidFileNameChars().Contains(f))
}

Lub

bool IsFileNameCorrect(string fileName){
    return fileName.All(f=>!Path.GetInvalidFileNameChars().Contains(f))
}
tmt
źródło
Czy chodziło Ci o: "return! FileName.Any (f => Path.GetInvalidFileNameChars (). Contains (f));" ?
Jack Griffin
@JackGriffin Oczywiście! Dziękuję za uwagę.
tmt
Chociaż ten kod jest bardzo przyjemny do odczytania, powinniśmy wziąć pod uwagę przykrości wewnętrzne Path.GetInvalidFileNameChars. Spójrz tutaj: referenceource.microsoft.com/#mscorlib/system/io/path.cs,289 - dla każdego twojego znaku fileNametworzony jest klon tablicy.
Piotr Zierhoffer
„DD: \\\\\ AAA ..... AAAA”. Nieważne, ale dla twojego kodu tak.
Ciccio Pasticcio
8

Microsoft Windows: jądro systemu Windows zabrania używania znaków z zakresu 1-31 (tj. 0x01-0x1F) i znaków „*: <>? \ |. Chociaż system NTFS dopuszcza, aby każdy składnik ścieżki (katalog lub nazwa pliku) miał 255 znaków i o długości do około 32767 znaków, jądro Windows obsługuje tylko ścieżki o długości do 259 znaków. Dodatkowo Windows zabrania używania nazw urządzeń MS-DOS AUX, CLOCK $, COM1, COM2, COM3, COM4, ​​COM5, COM6, COM7, COM8, COM9, CON, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9, NUL i PRN, a także te nazwy z dowolnym rozszerzeniem (na przykład AUX.txt), z wyjątkiem używania Długie ścieżki UNC (np. \. \ C: \ nul.txt lub \? \ D: \ aux \ con). (W rzeczywistości CLOCK $ może być używany, jeśli podano rozszerzenie). Te ograniczenia dotyczą tylko systemu Windows - Na przykład Linux pozwala na użycie „*: <>? \ | nawet w NTFS.

Źródło: http://en.wikipedia.org/wiki/Filename

Martin Faartoft
źródło
1
Mogę stworzyć plik o nazwie „CLOCK $”. Windows 7.
rory.ap
7

Zamiast jawnie uwzględniać wszystkie możliwe znaki, możesz użyć wyrażenia regularnego, aby sprawdzić obecność niedozwolonych znaków, a następnie zgłosić błąd. W idealnym przypadku aplikacja powinna nazywać pliki dokładnie tak, jak sobie tego życzy użytkownik, i płakać tylko wtedy, gdy natknie się na błąd.

Conroy P.
źródło
6

Pytanie brzmi, czy próbujesz ustalić, czy nazwa ścieżki jest prawidłową ścieżką systemu Windows, czy też jest legalna w systemie, w którym działa kod. ? Myślę, że to drugie jest ważniejsze, więc osobiście prawdopodobnie zdekomponowałbym pełną ścieżkę i spróbowałbym użyć _mkdir do utworzenia katalogu, do którego należy plik, a następnie spróbuję utworzyć plik.

W ten sposób wiesz nie tylko, czy ścieżka zawiera tylko prawidłowe znaki systemu Windows, ale także czy faktycznie reprezentuje ścieżkę, którą można zapisać w tym procesie.

kfh
źródło
6

Używam tego, aby pozbyć się nieprawidłowych znaków w nazwach plików bez rzucania wyjątków:

private static readonly Regex InvalidFileRegex = new Regex(
    string.Format("[{0}]", Regex.Escape(@"<>:""/\|?*")));

public static string SanitizeFileName(string fileName)
{
    return InvalidFileRegex.Replace(fileName, string.Empty);
}
JoelFan
źródło
5

Również CON, PRN, AUX, NUL, COM # i kilka innych nigdy nie są legalnymi nazwami plików w żadnym katalogu z jakimkolwiek rozszerzeniem.


źródło
1
To tylko połowa prawdy. Możesz tworzyć pliki z tymi nazwami, wywołując wersję Unicode narzędzia CreateFile (poprzedzając nazwę pliku znakiem „\\? \”).
Werner Henze
To stwierdzenie jest niekompletne i pomija LPT #
Thomas Weller,
4

Aby uzupełnić inne odpowiedzi, oto kilka dodatkowych skrajnych przypadków, które warto rozważyć.

Joe
źródło
3

W witrynie MSDN znajduje się lista niedozwolonych znaków:

Użyj prawie każdego znaku w bieżącej stronie kodowej dla nazwy, w tym znaków Unicode i znaków z rozszerzonego zestawu znaków (128–255), z wyjątkiem następujących:

  • Następujące znaki zastrzeżone są niedozwolone: ​​<>: "/ \ |? *
  • Znaki, których reprezentacje liczb całkowitych mieszczą się w zakresie od zera do 31, są niedozwolone.
  • Każdy inny znak, na który nie zezwala docelowy system plików.
Mark Biek
źródło
2

Ważny jest również docelowy system plików.

W systemie NTFS niektórych plików nie można tworzyć w określonych katalogach. EG $ Uruchom w katalogu głównym

Dominik Weber
źródło
2
Z pewnością nie wynika to z reguły nazewnictwa NTFS, ale tylko dlatego, że plik o nazwie $Bootjuż istnieje w katalogu?
Christian Hayter
2

To jest już pytanie, na które udzielono już odpowiedzi, ale ze względu na „Inne opcje”, oto pytanie nie idealne:

(nie jest to idealne rozwiązanie, ponieważ używanie wyjątków jako kontroli przepływu jest ogólnie „złą rzeczą”)

public static bool IsLegalFilename(string name)
{
    try 
    {
        var fileInfo = new FileInfo(name);
        return true;
    }
    catch
    {
        return false;
    }
}
JerKimball
źródło
Twój przykład nie zadziałał dla pliku CON (C: \ temp \ CON).
tcbrazil
Ale czy „C: \ temp \ CON” nie jest prawidłową nazwą pliku? Dlaczego by tak nie było?
Mark A. Donohoe
@MarqueIV - nie, to nieważne. Przeczytaj wszystkie odpowiedzi i komentarze powyżej lub wypróbuj sam i zobacz.
rory.ap
@Jer, „/ example” nie jest legalne, ale Twoja metoda zwraca true.
rory.ap
Aaaah ... przegapiłem część „CON”. Sama nazwa jest poprawna z punktu widzenia łańcucha (do którego odnosiłem się), ale teraz widzę, że CON jest nazwą zarezerwowaną, co czyni ją nieważną z punktu widzenia systemu Windows. Mój błąd.
Mark A. Donohoe,
2

W tej sytuacji wyrażenia regularne są przesadą. Możesz użyć tej String.IndexOfAny()metody w połączeniu z Path.GetInvalidPathChars()i Path.GetInvalidFileNameChars().

Zauważ również, że obie Path.GetInvalidXXX()metody klonują wewnętrzną tablicę i zwracają clone. Więc jeśli zamierzasz robić to dużo (tysiące i tysiące razy), możesz buforować kopię nieprawidłowej tablicy znaków w celu ponownego wykorzystania.

nhahtdh
źródło
2

Jeśli próbujesz tylko sprawdzić, czy ciąg zawierający nazwę / ścieżkę pliku zawiera nieprawidłowe znaki, najszybszą metodą, jaką znalazłem, jest Split()podzielenie nazwy pliku na tablicę części, gdziekolwiek jest nieprawidłowy znak. Jeśli wynik jest tylko tablicą 1, nie ma nieprawidłowych znaków. :-)

var nameToTest = "Best file name \"ever\".txt";
bool isInvalidName = nameToTest.Split(System.IO.Path.GetInvalidFileNameChars()).Length > 1;

var pathToTest = "C:\\My Folder <secrets>\\";
bool isInvalidPath = pathToTest.Split(System.IO.Path.GetInvalidPathChars()).Length > 1;

Próbowałem uruchomić tę i inne metody wymienione powyżej na nazwie pliku / ścieżki 1000000 razy w LinqPad.

Używanie Split()trwa tylko ~ 850 ms.

Użycie Regex("[" + Regex.Escape(new string(System.IO.Path.GetInvalidPathChars())) + "]")trwa około 6 sekund.

Bardziej skomplikowane wyrażenia regularne są DUŻO gorzej, podobnie jak niektóre inne opcje, takie jak użycie różnych metod Pathklasy w celu uzyskania nazwy pliku i umożliwienia ich wewnętrznej walidacji (najprawdopodobniej z powodu narzutu obsługi wyjątków).

To prawda, że ​​niezbyt często trzeba sprawdzać poprawność 1 miliona nazw plików, więc i tak pojedyncza iteracja jest odpowiednia dla większości tych metod. Ale nadal jest dość wydajny i skuteczny, jeśli szukasz tylko nieprawidłowych znaków.

Nick Albrecht
źródło
1

wiele z tych odpowiedzi nie zadziała, jeśli nazwa pliku jest zbyt długa i działa w środowisku starszym niż Windows 10. Podobnie zastanów się, co chcesz zrobić z kropkami - zezwalanie na początkowe lub końcowe jest poprawne technicznie, ale może powodować problemy, jeśli nie chcesz, aby plik był odpowiednio trudny do zobaczenia lub usunięcia.

To jest atrybut walidacji, który utworzyłem, aby sprawdzić poprawną nazwę pliku.

public class ValidFileNameAttribute : ValidationAttribute
{
    public ValidFileNameAttribute()
    {
        RequireExtension = true;
        ErrorMessage = "{0} is an Invalid Filename";
        MaxLength = 255; //superseeded in modern windows environments
    }
    public override bool IsValid(object value)
    {
        //http://stackoverflow.com/questions/422090/in-c-sharp-check-that-filename-is-possibly-valid-not-that-it-exists
        var fileName = (string)value;
        if (string.IsNullOrEmpty(fileName)) { return true;  }
        if (fileName.IndexOfAny(Path.GetInvalidFileNameChars()) > -1 ||
            (!AllowHidden && fileName[0] == '.') ||
            fileName[fileName.Length - 1]== '.' ||
            fileName.Length > MaxLength)
        {
            return false;
        }
        string extension = Path.GetExtension(fileName);
        return (!RequireExtension || extension != string.Empty)
            && (ExtensionList==null || ExtensionList.Contains(extension));
    }
    private const string _sepChar = ",";
    private IEnumerable<string> ExtensionList { get; set; }
    public bool AllowHidden { get; set; }
    public bool RequireExtension { get; set; }
    public int MaxLength { get; set; }
    public string AllowedExtensions {
        get { return string.Join(_sepChar, ExtensionList); } 
        set {
            if (string.IsNullOrEmpty(value))
            { ExtensionList = null; }
            else {
                ExtensionList = value.Split(new char[] { _sepChar[0] })
                    .Select(s => s[0] == '.' ? s : ('.' + s))
                    .ToList();
            }
    } }

    public override bool RequiresValidationContext => false;
}

i testy

[TestMethod]
public void TestFilenameAttribute()
{
    var rxa = new ValidFileNameAttribute();
    Assert.IsFalse(rxa.IsValid("pptx."));
    Assert.IsFalse(rxa.IsValid("pp.tx."));
    Assert.IsFalse(rxa.IsValid("."));
    Assert.IsFalse(rxa.IsValid(".pp.tx"));
    Assert.IsFalse(rxa.IsValid(".pptx"));
    Assert.IsFalse(rxa.IsValid("pptx"));
    Assert.IsFalse(rxa.IsValid("a/abc.pptx"));
    Assert.IsFalse(rxa.IsValid("a\\abc.pptx"));
    Assert.IsFalse(rxa.IsValid("c:abc.pptx"));
    Assert.IsFalse(rxa.IsValid("c<abc.pptx"));
    Assert.IsTrue(rxa.IsValid("abc.pptx"));
    rxa = new ValidFileNameAttribute { AllowedExtensions = ".pptx" };
    Assert.IsFalse(rxa.IsValid("abc.docx"));
    Assert.IsTrue(rxa.IsValid("abc.pptx"));
}
Brent
źródło
1

Moja próba:

using System.IO;

static class PathUtils
{
  public static string IsValidFullPath([NotNull] string fullPath)
  {
    if (string.IsNullOrWhiteSpace(fullPath))
      return "Path is null, empty or white space.";

    bool pathContainsInvalidChars = fullPath.IndexOfAny(Path.GetInvalidPathChars()) != -1;
    if (pathContainsInvalidChars)
      return "Path contains invalid characters.";

    string fileName = Path.GetFileName(fullPath);
    if (fileName == "")
      return "Path must contain a file name.";

    bool fileNameContainsInvalidChars = fileName.IndexOfAny(Path.GetInvalidFileNameChars()) != -1;
    if (fileNameContainsInvalidChars)
      return "File name contains invalid characters.";

    if (!Path.IsPathRooted(fullPath))
      return "The path must be absolute.";

    return "";
  }
}

To nie jest idealne, ponieważ Path.GetInvalidPathCharsnie zwraca pełnego zestawu znaków, które są nieprawidłowe w nazwach plików i katalogów, i oczywiście jest o wiele więcej subtelności.

Więc używam tej metody jako uzupełnienia:

public static bool TestIfFileCanBeCreated([NotNull] string fullPath)
{
  if (string.IsNullOrWhiteSpace(fullPath))
    throw new ArgumentException("Value cannot be null or whitespace.", "fullPath");

  string directoryName = Path.GetDirectoryName(fullPath);
  if (directoryName != null) Directory.CreateDirectory(directoryName);
  try
  {
    using (new FileStream(fullPath, FileMode.CreateNew)) { }
    File.Delete(fullPath);
    return true;
  }
  catch (IOException)
  {
    return false;
  }
}

Próbuje utworzyć plik i zwrócić false, jeśli wystąpi wyjątek. Oczywiście muszę utworzyć plik, ale myślę, że to najbezpieczniejszy sposób. Należy również pamiętać, że nie usuwam utworzonych katalogów.

Możesz również użyć pierwszej metody, aby przeprowadzić podstawową walidację, a następnie ostrożnie obsłużyć wyjątki, gdy używana jest ścieżka.

Maxence
źródło
0

Proponuję po prostu użyć Path.GetFullPath ()

string tagetFileFullNameToBeChecked;
try
{
  Path.GetFullPath(tagetFileFullNameToBeChecked)
}
catch(AugumentException ex)
{
  // invalid chars found
}
Tony Sun
źródło
Dodaj wyjaśnienie wraz z odpowiedzią, w jaki sposób ta odpowiedź pomaga OP w naprawianiu bieżącego problemu
ρяσѕρєя K
Zobacz dokument w MSDN dla AugumentExcpetion, czyta: ścieżka 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.
Tony Sun
W teorii (zgodnie z dokumentacją) to powinno działać, chociaż problem przynajmniej w .NET Core 3.1 nie.
Michel Jansson
0

Dostałem od kogoś ten pomysł. - nie wiem kto. Niech system operacyjny wykona ciężkie prace.

public bool IsPathFileNameGood(string fname)
{
    bool rc = Constants.Fail;
    try
    {
        this._stream = new StreamWriter(fname, true);
        rc = Constants.Pass;
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "Problem opening file");
        rc = Constants.Fail;
    }
    return rc;
}
KenR
źródło
0

Ten czek

static bool IsValidFileName(string name)
{
    return
        !string.IsNullOrWhiteSpace(name) &&
        name.IndexOfAny(Path.GetInvalidFileNameChars()) < 0 &&
        !Path.GetFullPath(name).StartsWith(@"\\.\");
}

odfiltrowuje nazw z nieprawidłowych znaków ( <>:"/\|?*i ASCII 0-31), a także zastrzeżonych urządzeń DOS ( CON, NUL, COMx). Pozwala na wiodące spacje i wszystkie kropki, zgodnie z Path.GetFullPath. (Utworzenie pliku ze spacjami wiodącymi kończy się pomyślnie w moim systemie).


Używany .NET Framework 4.7.1, testowany w systemie Windows 7.

Vlad
źródło
0

Jedna linijka do weryfikacji nielegalnych znaków w ciągu:

public static bool IsValidFilename(string testName) => !Regex.IsMatch(testName, "[" + Regex.Escape(new string(System.IO.Path.InvalidPathChars)) + "]");
Zananok
źródło
0

Moim zdaniem jedyną właściwą odpowiedzią na to pytanie jest próba użycia ścieżki i pozwolenie systemowi operacyjnemu i systemowi plików na jej walidację. W przeciwnym razie po prostu ponownie zaimplementujesz (i prawdopodobnie źle) wszystkie reguły walidacji, których system operacyjny i system plików już używają, a jeśli te reguły zostaną zmienione w przyszłości, będziesz musiał zmienić kod, aby je dopasować.

Igor Levicki
źródło
-1

Nazwy plików Windows są dość unrestrictive, tak naprawdę to nie może być nawet , że wiele kwestii. Znaki niedozwolone przez system Windows to:

\ / : * ? " < > |

Możesz łatwo napisać wyrażenie, aby sprawdzić, czy te znaki są obecne. Lepszym rozwiązaniem byłaby jednak próba nazwania plików zgodnie z życzeniem użytkownika i ostrzeganie ich, gdy nazwa pliku się nie przykleja.

Justin Poliey
źródło
Zabronione są również znaki <= 31.
Antymon