it-swarm-eu.dev

Jak zjistím, zda je daný řetězec v systému Windows?

Chci zahrnout funkci přejmenování dávkového souboru v mé aplikaci. Uživatel může zadat vzor cílového souboru a (po nahrazení některých zástupných znaků ve vzoru) musím zkontrolovat, zda to bude legální název souboru pod Windows. Snažil jsem se použít regulární výraz jako [a-zA-Z0-9_]+, ale neobsahuje mnoho národních specifických znaků z různých jazyků (např. Přehlásky apod.). Jaký je nejlepší způsob, jak takovou kontrolu udělat?

150
tomash

Seznam neplatných znaků můžete získat z Path.GetInvalidPathChars a GetInvalidFileNameChars .

UPD: Viz Návrh Steve Coopera o tom, jak je používat v regulárním výrazu.

UPD2: Všimněte si, že podle oddílu Poznámky v MSDN "Pole vrácené z této metody není zaručeno, že bude obsahovat úplnou sadu znaků, které jsou neplatné v názvech souborů a adresářů." Odpověď poskytovaná sixlettervaliables jde do více detailů.

96
Eugene Katz

Z "Pojmenování souboru nebo adresáře MSDN" zde jsou obecné konvence pro to, co je název právního souboru pod Windows:

Na aktuální kódové stránce můžete použít libovolný znak (Unicode/ANSI nad 127), s výjimkou:

  • <>:"/\|?*
  • Znaky, jejichž celočíselné reprezentace jsou 0-31 (méně než ASCII prostor)
  • Jakýkoli jiný znak, který cílový souborový systém neumožňuje (říkají, koncové úseky nebo mezery)
  • Libovolný z názvů 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 (a vyhnout se AUX.txt, atd.)
  • Název souboru je všechna období

Některé volitelné položky ke kontrole:

  • Cesty souborů (včetně názvu souboru) nemusí obsahovat více než 260 znaků (nepoužívají se předpona \?\)
  • Cesty souboru Unicode (včetně názvu souboru) s více než 32 000 znaky při použití \?\ (poznámka, že předpona může rozbalit složky adresáře a způsobit přetečení limitu 32 000)
116
user7116

Pro .Net Framework před 3.5 by to mělo fungovat:

Vyrovnávání regulárních výrazů by vám mělo pomoci. Zde je úryvek pomocí konstanty System.IO.Path.InvalidPathChars;

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;
}

Pro .Net Framework po 3.0 by to mělo fungovat:

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

Vyrovnávání regulárních výrazů by vám mělo pomoci. Zde je úryvek pomocí konstanty System.IO.Path.GetInvalidPathChars();

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;
}

Jakmile to víte, měli byste také zkontrolovat různé formáty, např. c:\my\drive a \\server\share\dir\file.ext

62
Steve Cooper

Pokuste se ji použít a pasti pro chybu. Povolená sada se může měnit v souborových systémech nebo v různých verzích systému Windows. Jinými slovy, pokud chcete vědět, zda se systému Windows líbí jméno, podejte mu jméno a řekněte mu to.

25

Tato třída vyčistí názvy souborů a cesty; použít jako 

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

Tady je kód;

/// <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();
    }

}
23
Steve Cooper

To je to, co používám:

    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));
    }

První vzor vytvoří regulární výraz obsahující neplatné/nelegální názvy souborů a znaků pro platformy Windows. Ten druhý dělá totéž, ale zajišťuje, že jméno je legální pro každou platformu.

22
Scott Dorman

Jeden rohový případ, který mě napadl, mě překvapilo, když jsem se o tom poprvé dozvěděl: Systém Windows umožňuje předávání znaků v názvech souborů! V systému Windows jsou například všechny legální a odlišné názvy souborů (bez uvozovek):

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

Jedna z těchto možností: Buďte opatrní při psaní kódu, který ořezává vedoucí/koncové mezery z názvu souboru.

18
Jon Schneider

Zjednodušení odpovědi Eugena Katze:

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

Nebo

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

Microsoft Windows: Jádro Windows zakazuje použití znaků v rozsahu 1-31 (tj. 0x01-0x1F) a znaků "*: <>?. Ačkoli NTFS umožňuje, aby každá složka cesty (adresář nebo název souboru) měla délku 255 znaků a dlouhé až 32767 znaků, jádro Windows podporuje pouze cesty až do délky 259 znaků a systém Windows navíc zakazuje použití názvů zařízení MS-DOS AUX, CLOCK $, COM1, COM2, COM3, COM4, ​​COM5, COM6,\t COM7, COM8, COM9, CON, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT5, LPT6, LPT7, LPT8, LPT9, NUL a PRN, stejně jako tyto názvy s libovolnou příponou (například AUX.txt), s výjimkou použití Dlouhé cesty UNC (např. C: nul.txt nebo D: aux) (Ve skutečnosti lze CLOCK $ použít, pokud je k dispozici přípona.) Tato omezení platí pouze pro Windows - Například Linux umožňuje použití "*: <>?"\| i v NTFS.

Zdroj: http://en.wikipedia.org/wiki/Filename

8
Martin Faartoft

Namísto explicitního zahrnutí všech možných znaků můžete provést regex, abyste zkontrolovali přítomnost nelegálních znaků, a poté chybu nahláste. V ideálním případě by vaše aplikace měla pojmenovat soubory přesně tak, jak si uživatel přeje, a křičet pouze v případě, že narazí na chybu.

7
ConroyP

Používám to, abych se zbavil (a) neplatných znaků v názvech souborů, aniž bych vyhrál výjimky:

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

public static string SanitizeFileName(string fileName)
{
    return InvalidFileRegex.Replace(fileName, string.Empty);
}
6
JoelFan

Také CON, PRN, AUX, NUL, COM # a několik dalších nejsou nikdy legální názvy souborů v žádném adresáři s žádným rozšířením.

5
Roland Rabien

Otázkou je, zda se snažíte zjistit, zda je název cesty legální cestou systému Windows, nebo zda je legální v systému, kde je kód spuštěn.? Myslím, že ten druhý je důležitější, takže osobně bych pravděpodobně rozložil celou cestu a pokusil se použít _mkdir k vytvoření adresáře, do kterého soubor patří, a pokuste se soubor vytvořit.

Tímto způsobem víte nejen v případě, že cesta obsahuje pouze platné znaky systému Windows, ale pokud skutečně představuje cestu, kterou lze tímto procesem zapsat.

5
kfh

Pro doplnění dalších odpovědí je zde několik dalších případů Edge, které byste měli zvážit.

4
Joe

Z MSDN , zde je seznam znaků, které nejsou povoleny:

Použít téměř libovolný znak na aktuální kódové stránce pro název, včetně znaků a znaků Unicode v rozšířené znakové sadě (128–255), s výjimkou následujících:

  • Následující vyhrazené znaky nejsou povoleny: <>: "/\T
  • Znaky, jejichž celočíselná reprezentace jsou v rozsahu od nuly do 31, nejsou povoleny.
  • Jakýkoli jiný znak, který cílový souborový systém neumožňuje.
3
Mark Biek

Regulární výrazy jsou pro tuto situaci přehnané. Metodu String.IndexOfAny() můžete použít v kombinaci s Path.GetInvalidPathChars() a Path.GetInvalidFileNameChars().

Všimněte si také, že obě metody Path.GetInvalidXXX() klonují vnitřní pole a vrátí klon. Takže pokud to budete dělat hodně (tisíce a tisíce krát), můžete si zkopírovat kopii pole pro neplatné znaky pro opakované použití.

2
s n

Důležitý je také cílový souborový systém.

V systému NTFS nelze některé soubory vytvořit v konkrétních adresářích. E.G. $ Spuštění v kořenovém adresáři 

2
Dominik Weber

Toto je již zodpovězená otázka, ale jen kvůli "dalším možnostem", zde je to ne ideální:

(non-ideal, protože použití výjimek jako řízení toku je "Bad Thing", obecně)

public static bool IsLegalFilename(string name)
{
    try 
    {
        var fileInfo = new FileInfo(name);
        return true;
    }
    catch
    {
        return false;
    }
}
2
JerKimball

Pokud se jen snažíte zkontrolovat, zda řetězec, který obsahuje název souboru/cestu, obsahuje neplatné znaky, nejrychlejší metodou, kterou jsem našel, je použít soubor Split() k rozdělení názvu souboru na pole částí, kde je neplatný znak. Pokud je výsledkem pouze pole 1, neexistují žádné neplatné znaky. :-)

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;

Pokusil jsem se spustit tento a další metody uvedené výše na názvu souboru/cesty 1 000 000krát v LinqPad.

Použití Split() je pouze ~ 850ms.

Použití Regex("[" + Regex.Escape(new string(System.IO.Path.GetInvalidPathChars())) + "]") je přibližně 6 sekund.

Komplikovanější regulární výrazy jsou mnohem horší, stejně jako některé z dalších možností, jako je použití různých metod ve třídě Path k získání názvu souboru a nechat jejich interní validaci provést práci (s největší pravděpodobností kvůli režii výjimek).

Uděluje se, že to není příliš často budete muset ověřit 1 milion jmen souborů, takže jedna iterace je v pořádku pro většinu těchto metod stejně. Je však stále velmi efektivní a efektivní, pokud hledáte pouze neplatné znaky.

1
Nick Albrecht

mnoho z těchto odpovědí nebude fungovat, pokud je název souboru příliš dlouhý a je spuštěn v prostředí před Windows 10. Podobně si zamyslete nad tím, co chcete dělat s obdobími - umožnění vedení nebo ukončení je technicky platné, ale můžete vytvořit problémy, pokud nechcete, aby byl soubor obtížně viditelný nebo vymazán.

Toto je atribut validace, který jsem vytvořil pro kontrolu platného názvu souboru. 

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;
}

a zkoušky

[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"));
}
1
Brent

Můj pokus:

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 není dokonalé, protože Path.GetInvalidPathChars nevrací úplnou sadu znaků, které jsou neplatné v názvech souborů a adresářů a samozřejmě existuje spousta dalších jemností.

Tuto metodu používám jako doplněk:

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;
  }
}

Pokusí se vytvořit soubor a vrátit false, pokud existuje výjimka. Samozřejmě musím vytvořit soubor, ale myslím, že je to nejbezpečnější způsob, jak to udělat. Všimněte si také, že nemám smazané adresáře, které byly vytvořeny.

Můžete také použít první metodu k provedení základního ověření a poté pečlivě zpracovat výjimky při použití cesty.

1
Maxence

Tato kontrola

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

filtruje jména s neplatnými znaky (<>:"/\|?* a ASCII 0-31), stejně jako vyhrazená zařízení DOS (CON, NUL, COMx). Umožňuje vést mezery a názvy všech teček v souladu s Path.GetFullPath. (Vytvoření souboru s předními prostory úspěšné v mém systému).


Použitý .NET Framework 4.7.1, testován na Windows 7.

0
Vlad

Dostal jsem ten nápad od někoho. - Nevím, kdo. Nechte OS dělat těžké zvedání.

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;
}
0
KenR

Jedna linka pro ověření nelegálních znaků v řetězci:

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

Doporučuji použít Path.GetFullPath ()

string tagetFileFullNameToBeChecked;
try
{
  Path.GetFullPath(tagetFileFullNameToBeChecked)
}
catch(AugumentException ex)
{
  // invalid chars found
}
0
Tony Sun