경로와 파일 이름에서 불법 문자를 제거하는 방법은 무엇입니까?
문제
간단한 문자열에서 불법 경로를 제거하고 문자를 파일을 제거하는 강력하고 간단한 방법이 필요합니다. 아래 코드를 사용했지만 아무것도하지 않는 것 같습니다. 무엇이 누락 되었습니까?
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 스 니펫에 대한 링크가 포함되어있어서 메소드를 검증 할 수 있습니다.
다음과 같은 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;
}
몇 가지 제안을 결합한 확장 방법을 만들었습니다.
- 해시 세트에 불법 캐릭터를 잡고 있습니다
- ASCII 127 이하의 문자를 필터링합니다. getInvalidFilenamechars는 0에서 255 사이의 ASCII 코드와 함께 가능한 모든 유효하지 않은 문자를 포함하지 않습니다. 여기를 봐 그리고 MSDN
- 대체 문자를 정의 할 가능성
원천:
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();