경로와 파일 이름에서 불법 문자를 제거하는 방법은 무엇입니까?

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

  •  02-07-2019
  •  | 
  •  

문제

간단한 문자열에서 불법 경로를 제거하고 문자를 파일을 제거하는 강력하고 간단한 방법이 필요합니다. 아래 코드를 사용했지만 아무것도하지 않는 것 같습니다. 무엇이 누락 되었습니까?

using System;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string illegal = "\"M<>\"\\a/ry/ h**ad:>> a\\/:*?\"<>| li*tt|le|| la\"mb.?";

            illegal = illegal.Trim(Path.GetInvalidFileNameChars());
            illegal = illegal.Trim(Path.GetInvalidPathChars());

            Console.WriteLine(illegal);
            Console.ReadLine();
        }
    }
}
도움이 되었습니까?

해결책

대신 이와 같은 것을 시도하십시오.

string illegal = "\"M\"\\a/ry/ h**ad:>> a\\/:*?\"| li*tt|le|| la\"mb.?";
string invalid = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());

foreach (char c in invalid)
{
    illegal = illegal.Replace(c.ToString(), ""); 
}

그러나 나는 그 의견에 동의해야한다. 나는 아마도 불법적 인 길을 다루려고 노력할 것이다.

편집 : 또는 Regex를 사용하여 잠재적으로 '더 나은'솔루션.

string illegal = "\"M\"\\a/ry/ h**ad:>> a\\/:*?\"| li*tt|le|| la\"mb.?";
string regexSearch = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
Regex r = new Regex(string.Format("[{0}]", Regex.Escape(regexSearch)));
illegal = r.Replace(illegal, "");

그럼에도 불구하고, 질문은 왜 먼저이 일을하고 있는지에 대해 간청합니다.

다른 팁

public string GetSafeFilename(string filename)
{

    return string.Join("_", filename.Split(Path.GetInvalidFileNameChars()));

}

이 답변은 Ceres의 다른 스레드에있었습니다, 나는 그것을 깔끔하고 단순하게 좋아합니다.

LINQ를 사용하여 파일 이름을 정리합니다. 유효한 경로를 확인하기 위해 이것을 쉽게 확장 할 수 있습니다.

private static string CleanFileName(string fileName)
{
    return Path.GetInvalidFileNameChars().Aggregate(fileName, (current, c) => current.Replace(c.ToString(), string.Empty));
}

업데이트

일부 의견에 따르면이 메소드가 작동하지 않으므로 DotNetFiddle 스 니펫에 대한 링크가 포함되어있어서 메소드를 검증 할 수 있습니다.

https://dotnetfiddle.net/nw1swy

다음과 같은 LINQ를 사용하여 불법 숯을 제거 할 수 있습니다.

var invalidChars = Path.GetInvalidFileNameChars();

var invalidCharsRemoved = stringWithInvalidChars
.Where(x => !invalidChars.Contains(x))
.ToArray();

편집하다
이것은 주석에 언급 된 필수 편집으로 보이는 방법입니다.

var invalidChars = Path.GetInvalidFileNameChars();

string invalidCharsRemoved = new string(stringWithInvalidChars
  .Where(x => !invalidChars.Contains(x))
  .ToArray());

이것들은 모두 훌륭한 솔루션이지만 모두 의존합니다. Path.GetInvalidFileNameChars, 생각만큼 신뢰할 수 없을 수도 있습니다. MSDN 문서의 다음 언급에 주목하십시오 Path.GetInvalidFileNameChars:

이 방법에서 반환 된 배열은 다음과 같습니다 파일 및 디렉토리 이름에 유효하지 않은 전체 문자 세트가 포함되어 있지는 않습니다. 잘못된 문자의 전체 세트는 파일 시스템에 따라 다를 수 있습니다. 예를 들어, Windows 기반 데스크탑 플랫폼에서 잘못된 경로 문자는 ASCII/Unicode 문자 1 ~ 31 개뿐만 아니라 인용 ( "), (<), (<), (>), 파이프 (|), 백 스페이스를 포함 할 수 있습니다. b), null ( 0) 및 탭 ( t).

더 나은 것은 아닙니다 Path.GetInvalidPathChars 방법. 그것은 똑같은 말을 포함합니다.

파일 이름 :

string cleanFileName = String.Join("", fileName.Split(Path.GetInvalidFileNameChars()));

전체 경로의 경우 :

string cleanPath = String.Join("", path.Split(Path.GetInvalidPathChars()));

이를 보안 기능으로 사용하려는 경우,보다 강력한 접근 방식은 모든 경로를 확장 한 다음 사용자가 제공 한 경로가 실제로 사용자가 액세스 해야하는 디렉토리의 자녀인지 확인하는 것입니다.

우선, 트림은 문자열의 시작 또는 끝에서 문자를 제거합니다.. 둘째, 공격적인 캐릭터를 실제로 제거하려는 지 평가하거나 빠르게 실패하고 사용자에게 파일 이름이 유효하지 않다는 것을 알리십시오. 내 선택은 후자이지만, 내 대답은 적어도 옳고 잘못된 방식으로 일을하는 방법을 보여 주어야합니다.

주어진 문자열이 유효한 파일 이름인지 확인하는 방법을 보여주는 stackoverflow 질문. 참고이 질문의 Regex를 사용하여 정규 표현식 교체로 문자를 제거 할 수 있습니다 (실제로 해야하는 경우).

나는 이것을 달성하기 위해 정규 표현을 사용합니다. 먼저, 나는 Regex를 동적으로 구축합니다.

string regex = string.Format(
                   "[{0}]",
                   Regex.Escape(new string(Path.GetInvalidFileNameChars())));
Regex removeInvalidChars = new Regex(regex, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant);

그런 다음 removeinvalidchars.replace에 전화하여 찾아서 교체하십시오. 이것은 경로 숯을 덮기 위해 분명히 확장 될 수 있습니다.

사용자 입력에서 불법적 문자를 제거하는 가장 좋은 방법은 Regex 클래스를 사용하여 불법 문자를 대체하거나 코드 뒤에 코드에서 메소드를 작성하거나 Regularexpression 컨트롤을 사용하여 클라이언트 측에서 검증하는 것입니다.

public string RemoveSpecialCharacters(string str)
{
    return Regex.Replace(str, "[^a-zA-Z0-9_]+", "_", RegexOptions.Compiled);
}

또는

<asp:RegularExpressionValidator ID="regxFolderName" 
                                runat="server" 
                                ErrorMessage="Enter folder name with  a-z A-Z0-9_" 
                                ControlToValidate="txtFolderName" 
                                Display="Dynamic" 
                                ValidationExpression="^[a-zA-Z0-9_]*$" 
                                ForeColor="Red">

나는 Jeff Yates의 아이디어를 절대적으로 선호합니다. 약간 수정하면 완벽하게 작동합니다.

string regex = String.Format("[{0}]", Regex.Escape(new string(Path.GetInvalidFileNameChars())));
Regex removeInvalidChars = new Regex(regex, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant);

개선은 자동으로 생성 된 Regex를 피하는 것입니다.

.NET 3 이상에 도움이되는 코드 스 니펫이 있습니다.

using System.IO;
using System.Text.RegularExpressions;

public static class PathValidation
{
    private static string pathValidatorExpression = "^[^" + string.Join("", Array.ConvertAll(Path.GetInvalidPathChars(), x => Regex.Escape(x.ToString()))) + "]+$";
    private static Regex pathValidator = new Regex(pathValidatorExpression, RegexOptions.Compiled);

    private static string fileNameValidatorExpression = "^[^" + string.Join("", Array.ConvertAll(Path.GetInvalidFileNameChars(), x => Regex.Escape(x.ToString()))) + "]+$";
    private static Regex fileNameValidator = new Regex(fileNameValidatorExpression, RegexOptions.Compiled);

    private static string pathCleanerExpression = "[" + string.Join("", Array.ConvertAll(Path.GetInvalidPathChars(), x => Regex.Escape(x.ToString()))) + "]";
    private static Regex pathCleaner = new Regex(pathCleanerExpression, RegexOptions.Compiled);

    private static string fileNameCleanerExpression = "[" + string.Join("", Array.ConvertAll(Path.GetInvalidFileNameChars(), x => Regex.Escape(x.ToString()))) + "]";
    private static Regex fileNameCleaner = new Regex(fileNameCleanerExpression, RegexOptions.Compiled);

    public static bool ValidatePath(string path)
    {
        return pathValidator.IsMatch(path);
    }

    public static bool ValidateFileName(string fileName)
    {
        return fileNameValidator.IsMatch(fileName);
    }

    public static string CleanPath(string path)
    {
        return pathCleaner.Replace(path, "");
    }

    public static string CleanFileName(string fileName)
    {
        return fileNameCleaner.Replace(fileName, "");
    }
}

위의 대부분의 솔루션은 잘못된 경로와 파일 이름 모두에 대한 불법 숯을 결합합니다 (두 통화 모두 현재 동일한 숯 세트를 반환 할 때에도). Path+Filename을 Path and Filename에서 먼저 분할 한 다음 적절한 세트를 IF에 적용한 다음 다시 결합합니다.

WVD_VEGT

유효하지 않은 문자 인 단일 문자로 제거하거나 교체하면 충돌이 발생할 수 있습니다.

<abc -> abc
>abc -> abc

다음은 이것을 피하는 간단한 방법입니다.

public static string ReplaceInvalidFileNameChars(string s)
{
    char[] invalidFileNameChars = System.IO.Path.GetInvalidFileNameChars();
    foreach (char c in invalidFileNameChars)
        s = s.Replace(c.ToString(), "[" + Array.IndexOf(invalidFileNameChars, c) + "]");
    return s;
}

결과:

 <abc -> [1]abc
 >abc -> [2]abc

예외를 던져.

if ( fileName.IndexOfAny(Path.GetInvalidFileNameChars()) > -1 )
            {
                throw new ArgumentException();
            }

나는이 괴물을 재미로 썼다.

public static class FileUtility
{
    private const char PrefixChar = '%';
    private static readonly int MaxLength;
    private static readonly Dictionary<char,char[]> Illegals;
    static FileUtility()
    {
        List<char> illegal = new List<char> { PrefixChar };
        illegal.AddRange(Path.GetInvalidFileNameChars());
        MaxLength = illegal.Select(x => ((int)x).ToString().Length).Max();
        Illegals = illegal.ToDictionary(x => x, x => ((int)x).ToString("D" + MaxLength).ToCharArray());
    }

    public static string FilenameEncode(string s)
    {
        var builder = new StringBuilder();
        char[] replacement;
        using (var reader = new StringReader(s))
        {
            while (true)
            {
                int read = reader.Read();
                if (read == -1)
                    break;
                char c = (char)read;
                if(Illegals.TryGetValue(c,out replacement))
                {
                    builder.Append(PrefixChar);
                    builder.Append(replacement);
                }
                else
                {
                    builder.Append(c);
                }
            }
        }
        return builder.ToString();
    }

    public static string FilenameDecode(string s)
    {
        var builder = new StringBuilder();
        char[] buffer = new char[MaxLength];
        using (var reader = new StringReader(s))
        {
            while (true)
            {
                int read = reader.Read();
                if (read == -1)
                    break;
                char c = (char)read;
                if (c == PrefixChar)
                {
                    reader.Read(buffer, 0, MaxLength);
                    var encoded =(char) ParseCharArray(buffer);
                    builder.Append(encoded);
                }
                else
                {
                    builder.Append(c);
                }
            }
        }
        return builder.ToString();
    }

    public static int ParseCharArray(char[] buffer)
    {
        int result = 0;
        foreach (char t in buffer)
        {
            int digit = t - '0';
            if ((digit < 0) || (digit > 9))
            {
                throw new ArgumentException("Input string was not in the correct format");
            }
            result *= 10;
            result += digit;
        }
        return result;
    }
}

나는 모든 나쁜 캐릭터를 확인하는 대신에 레지스와 어떤 캐릭터가 허용되는지 지정하는 것이 훨씬 쉽다고 생각합니다. 이 링크를 참조하십시오.http://www.c-sharpcorner.com/uploadfile/prasad_1/regexppsd120620051717am/regexppsd.aspx http://www.windowsdevcenter.com/pub/a/oreilly/windows/news/csharp_0101.html

또한 "정규 표현 편집기"를 검색하면 많은 도움이됩니다. C#의 코드를 출력하는 주변이 있습니다.

이것은 O (n) 인 것처럼 보이며 문자열에 너무 많은 메모리를 소비하지 않습니다.

    private static readonly HashSet<char> invalidFileNameChars = new HashSet<char>(Path.GetInvalidFileNameChars());

    public static string RemoveInvalidFileNameChars(string name)
    {
        if (!name.Any(c => invalidFileNameChars.Contains(c))) {
            return name;
        }

        return new string(name.Where(c => !invalidFileNameChars.Contains(c)).ToArray());
    }

여기에서 답을 스캔하면 모두 **는 유효하지 않은 파일 이름 문자를 사용하는 것 같습니다.

물론, 이것은 미세 최적화 일 수 있지만 유효한 파일 이름이되기 위해 많은 수의 값을 확인하려는 사람의 이점을 위해, 유효하지 않은 숯의 해시를 구축하면 눈에 띄게 더 나은 성능을 가져올 것입니다.

나는 과거에 해시 세트 (또는 사전)가 목록보다 반복되는 얼마나 빨리 성능이 우수한 지 매우 놀랐습니다. 문자열의 경우 엄청나게 낮은 숫자 (메모리에서 약 5-7 항목)입니다. 대부분의 다른 간단한 데이터 (객체 참조, 숫자 등)를 사용하면 마법의 크로스 오버는 약 20 개의 항목 인 것 같습니다.

경로에는 40 개의 유효하지 않은 문자가 있습니다. 오늘 검색을했고 여기에 STACKOVERFLOW에 HASHSET이 40 개 항목에 대한 배열/목록의 절반 이상이 조금 넘는 시간을 보여주는 벤치 마크가 있습니다. https://stackoverflow.com/a/10762995/949129

소독 경로에 사용하는 도우미 클래스는 다음과 같습니다. 나는 왜 내가 멋진 교체 옵션을 가지고 있었는지 잊어 버렸지 만 귀여운 보너스로 거기에 있습니다.

추가 보너스 방법 "isvalidlocalpath"도 :)

(** 정규 표현을 사용하지 않는 사람들)

public static class PathExtensions
{
    private static HashSet<char> _invalidFilenameChars;
    private static HashSet<char> InvalidFilenameChars
    {
        get { return _invalidFilenameChars ?? (_invalidFilenameChars = new HashSet<char>(Path.GetInvalidFileNameChars())); }
    }


    /// <summary>Replaces characters in <c>text</c> that are not allowed in file names with the 
    /// specified replacement character.</summary>
    /// <param name="text">Text to make into a valid filename. The same string is returned if 
    /// it is valid already.</param>
    /// <param name="replacement">Replacement character, or NULL to remove bad characters.</param>
    /// <param name="fancyReplacements">TRUE to replace quotes and slashes with the non-ASCII characters ” and ⁄.</param>
    /// <returns>A string that can be used as a filename. If the output string would otherwise be empty, "_" is returned.</returns>
    public static string ToValidFilename(this string text, char? replacement = '_', bool fancyReplacements = false)
    {
        StringBuilder sb = new StringBuilder(text.Length);
        HashSet<char> invalids = InvalidFilenameChars;
        bool changed = false;

        for (int i = 0; i < text.Length; i++)
        {
            char c = text[i];
            if (invalids.Contains(c))
            {
                changed = true;
                char repl = replacement ?? '\0';
                if (fancyReplacements)
                {
                    if (c == '"') repl = '”'; // U+201D right double quotation mark
                    else if (c == '\'') repl = '’'; // U+2019 right single quotation mark
                    else if (c == '/') repl = '⁄'; // U+2044 fraction slash
                }
                if (repl != '\0')
                    sb.Append(repl);
            }
            else
                sb.Append(c);
        }

        if (sb.Length == 0)
            return "_";

        return changed ? sb.ToString() : text;
    }


    /// <summary>
    /// Returns TRUE if the specified path is a valid, local filesystem path.
    /// </summary>
    /// <param name="pathString"></param>
    /// <returns></returns>
    public static bool IsValidLocalPath(this string pathString)
    {
        // From solution at https://stackoverflow.com/a/11636052/949129
        Uri pathUri;
        Boolean isValidUri = Uri.TryCreate(pathString, UriKind.Absolute, out pathUri);
        return isValidUri && pathUri != null && pathUri.IsLoopback;
    }
}
public static class StringExtensions
      {
        public static string RemoveUnnecessary(this string source)
        {
            string result = string.Empty;
            string regex = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
            Regex reg = new Regex(string.Format("[{0}]", Regex.Escape(regex)));
            result = reg.Replace(source, "");
            return result;
        }
    }

메소드를 명확하게 사용할 수 있습니다.

파일 이름은 문자를 포함 할 수 없습니다 Path.GetInvalidPathChars(), + 그리고 # 기호 및 기타 특정 이름. 우리는 모든 수표를 하나의 클래스로 결합했습니다.

public static class FileNameExtensions
{
    private static readonly Lazy<string[]> InvalidFileNameChars =
        new Lazy<string[]>(() => Path.GetInvalidPathChars()
            .Union(Path.GetInvalidFileNameChars()
            .Union(new[] { '+', '#' })).Select(c => c.ToString(CultureInfo.InvariantCulture)).ToArray());


    private static readonly HashSet<string> ProhibitedNames = new HashSet<string>
    {
        @"aux",
        @"con",
        @"clock$",
        @"nul",
        @"prn",

        @"com1",
        @"com2",
        @"com3",
        @"com4",
        @"com5",
        @"com6",
        @"com7",
        @"com8",
        @"com9",

        @"lpt1",
        @"lpt2",
        @"lpt3",
        @"lpt4",
        @"lpt5",
        @"lpt6",
        @"lpt7",
        @"lpt8",
        @"lpt9"
    };

    public static bool IsValidFileName(string fileName)
    {
        return !string.IsNullOrWhiteSpace(fileName)
            && fileName.All(o => !IsInvalidFileNameChar(o))
            && !IsProhibitedName(fileName);
    }

    public static bool IsProhibitedName(string fileName)
    {
        return ProhibitedNames.Contains(fileName.ToLower(CultureInfo.InvariantCulture));
    }

    private static string ReplaceInvalidFileNameSymbols([CanBeNull] this string value, string replacementValue)
    {
        if (value == null)
        {
            return null;
        }

        return InvalidFileNameChars.Value.Aggregate(new StringBuilder(value),
            (sb, currentChar) => sb.Replace(currentChar, replacementValue)).ToString();
    }

    public static bool IsInvalidFileNameChar(char value)
    {
        return InvalidFileNameChars.Value.Contains(value.ToString(CultureInfo.InvariantCulture));
    }

    public static string GetValidFileName([NotNull] this string value)
    {
        return GetValidFileName(value, @"_");
    }

    public static string GetValidFileName([NotNull] this string value, string replacementValue)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            throw new ArgumentException(@"value should be non empty", nameof(value));
        }

        if (IsProhibitedName(value))
        {
            return (string.IsNullOrWhiteSpace(replacementValue) ? @"_" : replacementValue) + value; 
        }

        return ReplaceInvalidFileNameSymbols(value, replacementValue);
    }

    public static string GetFileNameError(string fileName)
    {
        if (string.IsNullOrWhiteSpace(fileName))
        {
            return CommonResources.SelectReportNameError;
        }

        if (IsProhibitedName(fileName))
        {
            return CommonResources.FileNameIsProhibited;
        }

        var invalidChars = fileName.Where(IsInvalidFileNameChar).Distinct().ToArray();

        if(invalidChars.Length > 0)
        {
            return string.Format(CultureInfo.CurrentCulture,
                invalidChars.Length == 1 ? CommonResources.InvalidCharacter : CommonResources.InvalidCharacters,
                StringExtensions.JoinQuoted(@",", @"'", invalidChars.Select(c => c.ToString(CultureInfo.CurrentCulture))));
        }

        return string.Empty;
    }
}

방법 GetValidFileName 모든 잘못된 데이터를 대체합니다 _.

Windows 파일 이름 지정에 대한 불법 숯에서 문자열을 정리하는 하나의 라이너 :

public static string CleanIllegalName(string p_testName) => new Regex(string.Format("[{0}]", Regex.Escape(new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars())))).Replace(p_testName, "");
public static bool IsValidFilename(string testName)
{
    return !new Regex("[" + Regex.Escape(new String(System.IO.Path.GetInvalidFileNameChars())) + "]").IsMatch(testName);
}

이것은 당신이 원하는 것을 원하고 충돌을 피할 것입니다.

 static string SanitiseFilename(string key)
    {
        var invalidChars = Path.GetInvalidFileNameChars();
        var sb = new StringBuilder();
        foreach (var c in key)
        {
            var invalidCharIndex = -1;
            for (var i = 0; i < invalidChars.Length; i++)
            {
                if (c == invalidChars[i])
                {
                    invalidCharIndex = i;
                }
            }
            if (invalidCharIndex > -1)
            {
                sb.Append("_").Append(invalidCharIndex);
                continue;
            }

            if (c == '_')
            {
                sb.Append("__");
                continue;
            }

            sb.Append(c);
        }
        return sb.ToString();

    }

나는 이미 완전한 대답이 아니라고 생각합니다 ... 답은 깨끗한 파일 이름이나 경로 만 설명합니다. 둘 다가 아닙니다. 내 해결책은 다음과 같습니다.

private static string CleanPath(string path)
{
    string regexSearch = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
    Regex r = new Regex(string.Format("[{0}]", Regex.Escape(regexSearch)));
    List<string> split = path.Split('\\').ToList();
    string returnValue = split.Aggregate(string.Empty, (current, s) => current + (r.Replace(s, "") + @"\"));
    returnValue = returnValue.TrimEnd('\\');
    return returnValue;
}

몇 가지 제안을 결합한 확장 방법을 만들었습니다.

  1. 해시 세트에 불법 캐릭터를 잡고 있습니다
  2. ASCII 127 이하의 문자를 필터링합니다. getInvalidFilenamechars는 0에서 255 사이의 ASCII 코드와 함께 가능한 모든 유효하지 않은 문자를 포함하지 않습니다. 여기를 봐 그리고 MSDN
  3. 대체 문자를 정의 할 가능성

원천:

public static class FileNameCorrector
{
    private static HashSet<char> invalid = new HashSet<char>(Path.GetInvalidFileNameChars());

    public static string ToValidFileName(this string name, char replacement = '\0')
    {
        var builder = new StringBuilder();
        foreach (var cur in name)
        {
            if (cur > 31 && cur < 128 && !invalid.Contains(cur))
            {
                builder.Append(cur);
            }
            else if (replacement != '\0')
            {
                builder.Append(replacement);
            }
        }

        return builder.ToString();
    }
}

아니면 그냥 할 수 있습니다

[YOUR STRING].Replace('\\', ' ').Replace('/', ' ').Replace('"', ' ').Replace('*', ' ').Replace(':', ' ').Replace('?', ' ').Replace('<', ' ').Replace('>', ' ').Replace('|', ' ').Trim();
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top