Windows에서 주어진 문자열이 합법적이고 유효한 파일 이름인지 어떻게 확인합니까?

StackOverflow https://stackoverflow.com/questions/62771

  •  09-06-2019
  •  | 
  •  

문제

내 애플리케이션에 배치 파일 이름 바꾸기 기능을 포함하고 싶습니다.사용자는 대상 파일 이름 패턴을 입력할 수 있으며 (패턴의 일부 와일드카드를 바꾼 후) Windows에서 해당 파일 이름이 적합한지 확인해야 합니다.나는 다음과 같은 정규식을 사용하려고 시도했습니다. [a-zA-Z0-9_]+ 하지만 다양한 언어의 국가별 문자가 많이 포함되어 있지 않습니다(예:움라우트 등).그러한 확인을 수행하는 가장 좋은 방법은 무엇입니까?

도움이 되었습니까?

해결책

다음에서 잘못된 문자 목록을 얻을 수 있습니다. Path.GetInvalidPathChars 그리고 GetInvalidFileNameChars.

UPD: 보다 스티브 쿠퍼의 제안 정규 표현식에서 이를 사용하는 방법에 대해 알아보세요.

UPD2: MSDN의 설명 섹션에 따르면 "이 메서드에서 반환된 배열은 파일 및 디렉터리 이름에서 유효하지 않은 전체 문자 집합을 포함한다고 보장되지 않습니다." sixlettervaliables가 제공한 답변 더 자세히 들어갑니다.

다른 팁

에서 MSDN의 "파일 또는 디렉터리 이름 지정" Windows에서 합법적인 파일 이름에 대한 일반적인 규칙은 다음과 같습니다.

다음을 제외하고 현재 코드 페이지(127 이상의 유니코드/ANSI)에서 모든 문자를 사용할 수 있습니다.

  • < > : " / \ | ? *
  • 정수 표현이 0-31(ASCII 공간 미만)인 문자
  • 대상 파일 시스템에서 허용하지 않는 기타 문자(예: 후행 마침표 또는 공백)
  • 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 (및 방지 AUX.txt 등)
  • 파일명은 모두 마침표입니다

확인해야 할 몇 가지 선택 사항:

  • 파일 경로(파일 이름 포함)는 260자를 초과할 수 없습니다( \?\ 접두사)
  • 사용 시 유니코드 파일 경로(파일명 포함)가 32,000자 이상 \?\ (접두사는 디렉터리 구성 요소를 확장하여 32,000개 제한을 초과할 수 있습니다.)

을 위한 3.5 이전의 .Net 프레임워크 이것은 작동합니다 :

정규식 일치를 사용하면 어느 정도 도움이 될 것입니다.다음은 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;
}

을 위한 .Net 프레임워크 3.0 이후 이것은 작동합니다 :

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

정규식 일치를 사용하면 어느 정도 도움이 될 것입니다.다음은 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;
}

이를 알고 나면 다른 형식도 확인해야 합니다. c:\my\drive 그리고 \\server\share\dir\file.ext

그것을 사용해 보고 오류를 잡아보세요.허용되는 세트는 파일 시스템이나 다양한 Windows 버전에서 변경될 수 있습니다.즉, Windows가 해당 이름을 좋아하는지 알고 싶다면 이름을 건네주고 알려주십시오.

이 클래스는 파일 이름과 경로를 정리합니다.처럼 사용

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

코드는 다음과 같습니다.

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

}

이것이 내가 사용하는 것입니다:

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

첫 번째 패턴은 Windows 플랫폼에 대해서만 유효하지 않은/잘못된 파일 이름과 문자를 포함하는 정규식을 생성합니다.두 번째는 동일한 작업을 수행하지만 이름이 모든 플랫폼에서 합법적인지 확인합니다.

명심해야 할 한 가지 특수한 사례는 제가 처음 이 사실을 알았을 때 저를 놀라게 했습니다.Windows에서는 파일 이름에 공백 문자를 넣을 수 있습니다!예를 들어, 다음은 Windows에서 모두 합법적이고 고유한 파일 이름입니다(따옴표 제외).

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

이에 대한 한 가지 시사점은 다음과 같습니다.파일 이름 문자열에서 선행/후행 공백을 잘라내는 코드를 작성할 때는 주의하십시오.

Eugene Katz의 답변을 단순화하면 다음과 같습니다.

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

또는

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

마이크로소프트 윈도우:Windows 커널은 1-31 범위(예: 0x01-0x1F)의 문자와 " * 문자의 사용을 금지합니다.< > ?\ |.NTFS에서는 각 경로 구성 요소(디렉터리 또는 파일 이름)의 길이가 255자이고 경로의 최대 길이가 약 32767자일 수 있지만 Windows 커널은 최대 259자 길이의 경로만 지원합니다.또한 Windows에서는 MS-DOS 장치 이름 AUX, CLOCK$, COM1, COM2, COM3, COM4, ​​COM5, COM6, COM7, COM8, COM9, CON, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9, NUL 및 PRN과 긴 UNC 경로(예:\.\C: ul.txt 또는 \?\D:\aux\con).(실제로 확장이 제공되면 CLOCK$을 사용할 수 있습니다.) 이러한 제한은 Windows에만 적용됩니다. 예를 들어 Linux에서는 " * 사용을 허용합니다.< > ?| NTF에서도.

원천: http://en.wikipedia.org/wiki/파일 이름

가능한 모든 문자를 명시적으로 포함하는 대신 정규식을 수행하여 잘못된 문자가 있는지 확인한 다음 오류를 보고할 수 있습니다.이상적으로 애플리케이션은 사용자가 원하는 대로 정확하게 파일 이름을 지정해야 하며 오류가 발생한 경우에만 파울을 외쳐야 합니다.

예외를 발생시키지 않고 파일 이름에서 유효하지 않은 문자를 제거하기 위해 이것을 사용합니다.

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

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

또한 CON, PRN, AUX, NUL, COM# 및 기타 몇 가지 다른 이름은 어떤 확장명을 가진 어떤 디렉터리에서도 합법적인 파일 이름이 아닙니다.

문제는 경로 이름이 유효한 Windows 경로인지 또는 유효한지 확인하려는 것입니다. 코드가 실행되는 시스템에서.?후자가 더 중요하다고 생각하기 때문에 개인적으로 전체 경로를 분해하고 _mkdir을 사용하여 파일이 속한 디렉터리를 만든 다음 파일을 생성해 보도록 하겠습니다.

이렇게 하면 경로에 유효한 Windows 문자만 포함되어 있는지뿐 아니라 실제로 이 프로세스에서 쓸 수 있는 경로를 나타내는지 알 수 있습니다.

다른 답변을 보완하기 위해 고려해야 할 몇 가지 추가적인 극단적 사례는 다음과 같습니다.

에서 MSDN, 허용되지 않는 문자 목록은 다음과 같습니다.

다음을 제외하고 유니코드 문자 및 확장 문자 세트(128-255)의 문자를 포함하여 현재 코드 페이지의 거의 모든 문자를 이름에 사용하십시오.

  • 다음 예약 문자는 허용되지 않습니다.< > :" / \ | ?*
  • 정수 표현이 0부터 31까지의 범위에 있는 문자는 허용되지 않습니다.
  • 대상 파일 시스템에서 허용하지 않는 기타 문자입니다.

또한 대상 파일 시스템도 중요합니다.

NTFS에서는 특정 디렉터리에 일부 파일을 만들 수 없습니다.예를 들어$루트에서 부팅

이것은 이미 답변된 질문이지만 "기타 옵션"을 위해 이상적이지 않은 질문은 다음과 같습니다.

(예외를 흐름 제어로 사용하는 것은 일반적으로 "나쁜 일"이기 때문에 이상적이지 않습니다)

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

이 상황에서는 정규 표현식이 과도합니다.당신은 사용할 수 있습니다 String.IndexOfAny() 와 결합된 방법 Path.GetInvalidPathChars() 그리고 Path.GetInvalidFileNameChars().

또한 둘 다 참고하세요 Path.GetInvalidXXX() 메서드는 내부 배열을 복제하고 복제본을 반환합니다.따라서 이 작업을 여러 번(수천 번) 수행하려는 경우 재사용을 위해 유효하지 않은 문자 배열의 복사본을 캐시할 수 있습니다.

파일 이름이 너무 길고 Windows 10 이전 환경에서 실행되는 경우 이러한 답변 중 대부분이 작동하지 않습니다.마찬가지로 마침표로 무엇을 하고 싶은지 생각해 보세요. 선행 또는 후행을 허용하는 것은 기술적으로 유효하지만 파일을 보거나 삭제하기 어렵게 만들고 싶지 않으면 문제가 발생할 수 있습니다.

이것은 유효한 파일 이름을 확인하기 위해 만든 유효성 검사 속성입니다.

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

그리고 테스트

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

파일 이름/경로를 포함하는 문자열에 잘못된 문자가 있는지 확인하려는 경우 내가 찾은 가장 빠른 방법은 다음을 사용하는 것입니다. Split() 유효하지 않은 문자가 있는 곳마다 파일 이름을 여러 부분의 배열로 분리합니다.결과가 1의 배열이면 유효하지 않은 문자가 없습니다.:-)

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;

나는 LinqPad에서 파일/경로 이름에 대해 위에서 언급한 이 방법과 다른 방법을 1,000,000번 실행해 보았습니다.

사용 Split() ~850ms에 불과합니다.

사용 Regex("[" + Regex.Escape(new string(System.IO.Path.GetInvalidPathChars())) + "]") 약 6초입니다.

더 복잡한 정규 표현식은 훨씬 더 나쁩니다. Path 클래스를 사용하여 파일 이름을 가져오고 내부 유효성 검사가 작업을 수행하도록 합니다(예외 처리 오버헤드로 인해 발생했을 가능성이 높음).

100만 개의 파일 이름을 검증해야 하는 경우가 그리 자주 발생하지 않는다는 점을 감안하면 어쨌든 대부분의 방법에서는 한 번의 반복으로 충분합니다.하지만 유효하지 않은 문자만 찾는 경우에는 여전히 매우 효율적이고 효과적입니다.

내 시도:

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

이것은 완벽하지 않습니다. 왜냐하면 Path.GetInvalidPathChars 파일 및 디렉터리 이름에 유효하지 않은 전체 문자 집합을 반환하지 않으며 물론 더 많은 미묘한 부분이 있습니다.

그래서 저는 이 방법을 보완적으로 사용합니다.

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

파일 생성을 시도하고 예외가 있으면 false를 반환합니다.물론 파일을 만들어야 하지만 그렇게 하는 것이 가장 안전한 방법이라고 생각합니다.또한 생성된 디렉터리를 삭제하는 것이 아니라는 점도 참고하세요.

첫 번째 방법을 사용하여 기본 유효성 검사를 수행한 다음 경로가 사용될 때 예외를 주의 깊게 처리할 수도 있습니다.

Path.GetFullPath()를 사용하는 것이 좋습니다.

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

누군가에게서 이런 아이디어를 얻었습니다.- 누군지 모르겠어요.OS가 무거운 작업을 수행하도록 하십시오.

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

이 수표

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

잘못된 문자가 포함된 이름을 필터링합니다(<>:"/\|?* 및 ASCII 0-31) 및 예약된 DOS 장치(CON, NUL, COMx).다음과 일치하는 선행 공백과 모든 점 이름을 허용합니다. Path.GetFullPath.(내 시스템에서는 선행 공백이 있는 파일 생성이 성공합니다.)


.NET Framework 4.7.1을 사용했으며 Windows 7에서 테스트되었습니다.

문자열에서 잘못된 문자를 확인하기 위한 하나의 라이너:

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

Windows 파일 이름은 매우 제한적이지 않으므로 실제로는 제한이 없을 수도 있습니다. 저것 문제가 많습니다.Windows에서 허용되지 않는 문자는 다음과 같습니다.

\ / : * ? " < > |

해당 문자가 있는지 확인하는 표현식을 쉽게 작성할 수 있습니다.하지만 더 나은 해결책은 사용자가 원하는 대로 파일 이름을 지정하고 파일 이름이 고정되지 않으면 경고하는 것입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top