경로 및 파일 이름에서 잘못된 문자를 제거하는 방법은 무엇입니까?
간단한 문자열에서 불법 경로 및 파일 문자를 제거할 수 있는 강력하고 간단한 방법이 필요합니다.아래 코드를 사용해봤는데 아무 효과가 없는 것 같은데, 무엇이 부족한가요?
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();
}
}
}
원래 질문은 "불법 문자 제거"였습니다.
public string RemoveInvalidChars(string filename)
{
return string.Concat(filename.Split(Path.GetInvalidFileNameChars()));
}
대신 다음과 같이 교체할 수 있습니다.
public string ReplaceInvalidChars(string filename)
{
return string.Join("_", filename.Split(Path.GetInvalidFileNameChars()));
}
이 대답은 Ceres의 다른 스레드에 있었습니다. 저는 깔끔하고 단순한 것을 정말 좋아합니다.
대신에 이런 것을 시도해 보세요;
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, "");
그럼에도 불구하고, 애초에 왜 이런 일을 하고 있는지에 대한 질문이 시작됩니다.
나는 파일 이름을 정리하기 위해 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
다음과 같이 Linkq를 사용하여 잘못된 문자를 제거할 수 있습니다.
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());
파일 이름의 경우:
var cleanFileName = string.Join("", fileName.Split(Path.GetInvalidFileNameChars()));
전체 경로의 경우:
var cleanPath = string.Join("", path.Split(Path.GetInvalidPathChars()));
이 기능을 보안 기능으로 사용하려면 모든 경로를 확장한 다음 사용자가 제공한 경로가 사용자가 액세스할 수 있는 디렉토리의 하위 경로인지 확인하는 것이 더 강력한 방법입니다.
해결책이지만, 은 모두 모훌해만지이결륭책모두, 그은들이것은에 합니다.Path.GetInvalidFileNameChars
당신이 생각하는 것만큼 신뢰할 수 없을 수도 있습니다.MSDN 설명서의 다음 주석에 유의하십시오.
이 메서드에서 반환된 배열에 파일 및 디렉터리 이름에 잘못된 문자 집합이 포함되어 있는지 확인할 수 없습니다.잘못된 문자의 전체 집합은 파일 시스템에 따라 다를 수 있습니다.예를 들어 윈도우즈 기반 데스크톱 플랫폼에서 잘못된 경로 문자에는 ASCII/Unicode 문자 1-31뿐만 아니라 따옴표("), 더 작은(<), 더 큰(>), 파이프(|), 백스페이스(\b), 널(\0) 및 탭(\t)이 포함될 수 있습니다.
방법으로는 더 이상 좋지 않습니다.그것은 정확히 같은 말을 포함하고 있습니다.
사용자 입력에서 잘못된 문자를 제거하는 가장 좋은 방법은 정규식 클래스를 사용하여 잘못된 문자를 바꾸거나 코드 뒤에 메서드를 만들거나 정규식 컨트롤을 사용하여 클라이언트 측에서 검증하는 것입니다.
public string RemoveSpecialCharacters(string str)
{
return Regex.Replace(str, "[^a-zA-Z0-9_]+", "_", RegexOptions.Compiled);
}
OR
<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">
먼저, 자르기는 문자열의 시작 부분이나 끝 부분에서만 문자를 제거합니다.두 번째로, 공격적인 문자를 제거할 것인지, 아니면 빨리 실패하고 파일 이름이 잘못되었음을 사용자에게 알릴 것인지를 평가해야 합니다.저의 선택은 후자입니다. 하지만 제 대답은 적어도 여러분에게 옳고 그른 방식으로 일을 처리하는 방법을 보여줘야 합니다.
지정된 문자열이 유효한 파일 이름인지 확인하는 방법을 보여주는 StackOverflow 질문입니다.이 질문의 정규식을 사용하여 정규식 대체 문자를 제거할 수 있습니다(정말 필요한 경우).
저는 이것을 달성하기 위해 정규 표현을 사용합니다.먼저, 저는 동적으로 정규식을 만듭니다.
string regex = string.Format(
"[{0}]",
Regex.Escape(new string(Path.GetInvalidFileNameChars())));
Regex removeInvalidChars = new Regex(regex, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant);
그런 다음 removeInvalidChars를 호출합니다.찾기 및 바꾸기를 수행합니다.이것은 분명히 경로 문자를 포함하도록 확장될 수 있습니다.
저는 제프 예이츠의 아이디어를 절대적으로 선호합니다.조금만 수정하면 완벽하게 작동합니다.
string regex = String.Format("[{0}]", Regex.Escape(new string(Path.GetInvalidFileNameChars())));
Regex removeInvalidChars = new Regex(regex, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant);
개선 사항은 자동으로 생성된 정규식을 벗어나는 것입니다.
다음은 .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을 분할한 다음 적절한 세트를 둘 중 하나에 적용한 다음 둘을 다시 결합합니다.
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();
}
이것은 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());
}
은 " 이름다문포수없습다니함할자파를음일은"의 문자를 할 수 .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
를 모든 잘된데이다바꿉로니다음으로 ._
.
이 괴물은 재미로 쓴 거예요 왕복할 수 있게 해줘요
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;
}
}
프로젝트의 여러 위치에서 메소드를 사용해야 하는 경우 확장 메소드를 만들어 프로젝트의 아무 곳에서나 문자열을 호출할 수 있습니다.
public static class StringExtension
{
public static string RemoveInvalidChars(this string originalString)
{
string finalString=string.Empty;
if (!string.IsNullOrEmpty(originalString))
{
return string.Concat(originalString.Split(Path.GetInvalidFileNameChars()));
}
return finalString;
}
}
위의 확장 메서드를 다음과 같이 호출할 수 있습니다.
string illegal = "\"M<>\"\\a/ry/ h**ad:>> a\\/:*?\"<>| li*tt|le|| la\"mb.?";
string afterIllegalChars = illegal.RemoveInvalidChars();
모든 잘못된 문자를 확인하려고 하는 것보다 정규식을 사용하고 어떤 문자가 허용되는지 지정하는 것이 훨씬 더 쉽다고 생각합니다.다음 링크를 참조하십시오. http://www.c-sharpcorner.com/UploadFile/prasad_1/RegExpPSD12062005021717AM/RegExpPSD.aspx http://www.windowsdevcenter.com/pub/a/oreilly/windows/news/csharp_0101.html
또한 "정규 표현 편집기"를 검색하면 많은 도움이 됩니다.당신을 위해 c# 코드를 출력하는 것도 있습니다.
여기서 답변을 스캔하면 모두 ** 잘못된 파일 이름 문자의 문자 배열을 사용하는 것과 관련이 있는 것 같습니다.
물론, 이것은 미시적으로 최적화될 수 있지만 유효한 파일 이름에 대한 많은 값을 확인하려는 사람들의 이익을 위해, 잘못된 문자의 해시 집합을 구축하면 현저하게 더 나은 성능을 얻을 수 있다는 것에 주목할 필요가 있습니다.
저는 과거에 해시 집합(또는 사전)이 목록을 반복하는 것을 얼마나 빨리 능가하는지 매우 놀랐습니다(충격적이었습니다).문자열을 사용하면 터무니없이 낮은 숫자(메모리에서 약 5-7개 항목)입니다.다른 대부분의 간단한 데이터(객체 참조, 숫자 등)의 경우 매직 크로스오버는 약 20개 항목으로 보입니다.
경로에 잘못된 문자가 40개 있습니다.FileNameChars "list"가 잘못되었습니다.오늘 검색을 해보니 여기 StackOverflow에 대한 꽤 좋은 벤치마크가 있는데, 이는 해시 집합이 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;
}
}
여기 저의 작은 기부금이 있습니다.새 문자열이나 문자열 작성기를 만들지 않고 동일한 문자열 내에서 바꾸는 메서드입니다.빠르고 이해하기 쉬우며 이 게시물의 모든 언급에 대한 좋은 대안입니다.
private static HashSet<char> _invalidCharsHash;
private static HashSet<char> InvalidCharsHash
{
get { return _invalidCharsHash ?? (_invalidCharsHash = new HashSet<char>(Path.GetInvalidFileNameChars())); }
}
private static string ReplaceInvalidChars(string fileName, string newValue)
{
char newChar = newValue[0];
char[] chars = fileName.ToCharArray();
for (int i = 0; i < chars.Length; i++)
{
char c = chars[i];
if (InvalidCharsHash.Contains(c))
chars[i] = newChar;
}
return new string(chars);
}
다음과 같이 부를 수 있습니다.
string illegal = "\"M<>\"\\a/ry/ h**ad:>> a\\/:*?\"<>| li*tt|le|| la\"mb.?";
string legal = ReplaceInvalidChars(illegal);
및 반환:
_M ____a_ry_ h__ad___ a_________ li_tt_le__ la_mb._
이 메서드는 항상 잘못된 문자를 지정된 값으로 대체하지만 제거하지는 않습니다.잘못된 문자를 제거하려면 다음과 같은 방법을 사용할 수 있습니다.
private static string RemoveInvalidChars(string fileName, string newValue)
{
char newChar = string.IsNullOrEmpty(newValue) ? char.MinValue : newValue[0];
bool remove = newChar == char.MinValue;
char[] chars = fileName.ToCharArray();
char[] newChars = new char[chars.Length];
int i2 = 0;
for (int i = 0; i < chars.Length; i++)
{
char c = chars[i];
if (InvalidCharsHash.Contains(c))
{
if (!remove)
newChars[i2++] = newChar;
}
else
newChars[i2++] = c;
}
return new string(newChars, 0, i2);
}
벤치마크
성능이 목적이라면 이 게시물에서 찾은 대부분의 방법으로 시간을 지정한 테스트 실행을 실행했습니다.OP가 문자열을 정리하도록 요청했기 때문에 이러한 메소드 중 일부는 지정된 문자로 대체되지 않습니다.원하는 시나리오에서 원하지 않는 문자만 제거하면 되는 경우 지정된 문자로 대체하는 테스트와 빈 문자로 대체하는 일부 테스트를 추가했습니다.이 벤치마크에 사용된 코드는 끝에 있으므로 직접 테스트를 실행할 수 있습니다.
Methods 참 : 방법Test1
그리고.Test2
둘 다 이 게시물에 제안되어 있습니다.
첫 번째 실행
replacing with '_', 1000000 iterations
결과:
============Test1===============
Elapsed=00:00:01.6665595
Result=_M ____a_ry_ h__ad___ a_________ li_tt_le__ la_mb._
============Test2===============
Elapsed=00:00:01.7526835
Result=_M ____a_ry_ h__ad___ a_________ li_tt_le__ la_mb._
============Test3===============
Elapsed=00:00:05.2306227
Result=_M ____a_ry_ h__ad___ a_________ li_tt_le__ la_mb._
============Test4===============
Elapsed=00:00:14.8203696
Result=_M ____a_ry_ h__ad___ a_________ li_tt_le__ la_mb._
============Test5===============
Elapsed=00:00:01.8273760
Result=_M ____a_ry_ h__ad___ a_________ li_tt_le__ la_mb._
============Test6===============
Elapsed=00:00:05.4249985
Result=_M ____a_ry_ h__ad___ a_________ li_tt_le__ la_mb._
============Test7===============
Elapsed=00:00:07.5653833
Result=_M ____a_ry_ h__ad___ a_________ li_tt_le__ la_mb._
============Test8===============
Elapsed=00:12:23.1410106
Result=_M ____a_ry_ h__ad___ a_________ li_tt_le__ la_mb._
============Test9===============
Elapsed=00:00:02.1016708
Result=_M ____a_ry_ h__ad___ a_________ li_tt_le__ la_mb._
============Test10===============
Elapsed=00:00:05.0987225
Result=M ary had a little lamb.
============Test11===============
Elapsed=00:00:06.8004289
Result=M ary had a little lamb.
세컨드 런
removing invalid chars, 1000000 iterations
참고: 테스트 1은 제거되지 않고 교체만 합니다.
결과:
============Test1===============
Elapsed=00:00:01.6945352
Result= M a ry h ad a li tt le la mb.
============Test2===============
Elapsed=00:00:01.4798049
Result=M ary had a little lamb.
============Test3===============
Elapsed=00:00:04.0415688
Result=M ary had a little lamb.
============Test4===============
Elapsed=00:00:14.3397960
Result=M ary had a little lamb.
============Test5===============
Elapsed=00:00:01.6782505
Result=M ary had a little lamb.
============Test6===============
Elapsed=00:00:04.9251707
Result=M ary had a little lamb.
============Test7===============
Elapsed=00:00:07.9562379
Result=M ary had a little lamb.
============Test8===============
Elapsed=00:12:16.2918943
Result=M ary had a little lamb.
============Test9===============
Elapsed=00:00:02.0770277
Result=M ary had a little lamb.
============Test10===============
Elapsed=00:00:05.2721232
Result=M ary had a little lamb.
============Test11===============
Elapsed=00:00:05.2802903
Result=M ary had a little lamb.
벤치마크 결과
방들Test1
,Test2
그리고.Test5
가장 빠릅니다. 법Test8
가장 느립니다.
코드
벤치마크의 전체 코드는 다음과 같습니다.
private static HashSet<char> _invalidCharsHash;
private static HashSet<char> InvalidCharsHash
{
get { return _invalidCharsHash ?? (_invalidCharsHash = new HashSet<char>(Path.GetInvalidFileNameChars())); }
}
private static string _invalidCharsValue;
private static string InvalidCharsValue
{
get { return _invalidCharsValue ?? (_invalidCharsValue = new string(Path.GetInvalidFileNameChars())); }
}
private static char[] _invalidChars;
private static char[] InvalidChars
{
get { return _invalidChars ?? (_invalidChars = Path.GetInvalidFileNameChars()); }
}
static void Main(string[] args)
{
string testPath = "\"M <>\"\\a/ry/ h**ad:>> a\\/:*?\"<>| li*tt|le|| la\"mb.?";
int max = 1000000;
string newValue = "";
TimeBenchmark(max, Test1, testPath, newValue);
TimeBenchmark(max, Test2, testPath, newValue);
TimeBenchmark(max, Test3, testPath, newValue);
TimeBenchmark(max, Test4, testPath, newValue);
TimeBenchmark(max, Test5, testPath, newValue);
TimeBenchmark(max, Test6, testPath, newValue);
TimeBenchmark(max, Test7, testPath, newValue);
TimeBenchmark(max, Test8, testPath, newValue);
TimeBenchmark(max, Test9, testPath, newValue);
TimeBenchmark(max, Test10, testPath, newValue);
TimeBenchmark(max, Test11, testPath, newValue);
Console.Read();
}
private static void TimeBenchmark(int maxLoop, Func<string, string, string> func, string testString, string newValue)
{
var sw = new Stopwatch();
sw.Start();
string result = string.Empty;
for (int i = 0; i < maxLoop; i++)
result = func?.Invoke(testString, newValue);
sw.Stop();
Console.WriteLine($"============{func.Method.Name}===============");
Console.WriteLine("Elapsed={0}", sw.Elapsed);
Console.WriteLine("Result={0}", result);
Console.WriteLine("");
}
private static string Test1(string fileName, string newValue)
{
char newChar = string.IsNullOrEmpty(newValue) ? char.MinValue : newValue[0];
char[] chars = fileName.ToCharArray();
for (int i = 0; i < chars.Length; i++)
{
if (InvalidCharsHash.Contains(chars[i]))
chars[i] = newChar;
}
return new string(chars);
}
private static string Test2(string fileName, string newValue)
{
char newChar = string.IsNullOrEmpty(newValue) ? char.MinValue : newValue[0];
bool remove = newChar == char.MinValue;
char[] chars = fileName.ToCharArray();
char[] newChars = new char[chars.Length];
int i2 = 0;
for (int i = 0; i < chars.Length; i++)
{
char c = chars[i];
if (InvalidCharsHash.Contains(c))
{
if (!remove)
newChars[i2++] = newChar;
}
else
newChars[i2++] = c;
}
return new string(newChars, 0, i2);
}
private static string Test3(string filename, string newValue)
{
foreach (char c in InvalidCharsValue)
{
filename = filename.Replace(c.ToString(), newValue);
}
return filename;
}
private static string Test4(string filename, string newValue)
{
Regex r = new Regex(string.Format("[{0}]", Regex.Escape(InvalidCharsValue)));
filename = r.Replace(filename, newValue);
return filename;
}
private static string Test5(string filename, string newValue)
{
return string.Join(newValue, filename.Split(InvalidChars));
}
private static string Test6(string fileName, string newValue)
{
return InvalidChars.Aggregate(fileName, (current, c) => current.Replace(c.ToString(), newValue));
}
private static string Test7(string fileName, string newValue)
{
string regex = string.Format("[{0}]", Regex.Escape(InvalidCharsValue));
return Regex.Replace(fileName, regex, newValue, RegexOptions.Compiled);
}
private static string Test8(string fileName, string newValue)
{
string regex = string.Format("[{0}]", Regex.Escape(InvalidCharsValue));
Regex removeInvalidChars = new Regex(regex, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant);
return removeInvalidChars.Replace(fileName, newValue);
}
private static string Test9(string fileName, string newValue)
{
StringBuilder sb = new StringBuilder(fileName.Length);
bool changed = false;
for (int i = 0; i < fileName.Length; i++)
{
char c = fileName[i];
if (InvalidCharsHash.Contains(c))
{
changed = true;
sb.Append(newValue);
}
else
sb.Append(c);
}
if (sb.Length == 0)
return newValue;
return changed ? sb.ToString() : fileName;
}
private static string Test10(string fileName, string newValue)
{
if (!fileName.Any(c => InvalidChars.Contains(c)))
{
return fileName;
}
return new string(fileName.Where(c => !InvalidChars.Contains(c)).ToArray());
}
private static string Test11(string fileName, string newValue)
{
string invalidCharsRemoved = new string(fileName
.Where(x => !InvalidChars.Contains(x))
.ToArray());
return invalidCharsRemoved;
}
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;
}
}
방법을 명확하게 사용할 수 있습니다.
윈도우즈 파일 이름 지정에 잘못된 문자에서 문자열을 정리하는 하나의 라이너:
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, "");
나는 여기에 게시된 다른 방법(특히 너무 느린 정규식)보다 훨씬 빠른 것처럼 보이지만 게시된 모든 방법을 테스트하지는 않았습니다.
https://dotnetfiddle.net/haIXiY
첫 번째 방법(내 방법)과 두 번째 방법(내 방법도 있지만 오래된 방법)도 백슬래시를 추가로 확인하기 때문에 벤치마크가 완벽하지는 않지만 어쨌든 아이디어를 제공하기 위한 것입니다.
노트북 결과(100,000회 반복):
StringHelper.RemoveInvalidCharacters 1: 451 ms
StringHelper.RemoveInvalidCharacters 2: 7139 ms
StringHelper.RemoveInvalidCharacters 3: 2447 ms
StringHelper.RemoveInvalidCharacters 4: 3733 ms
StringHelper.RemoveInvalidCharacters 5: 11689 ms (==> Regex!)
가장 빠른 방법:
public static string RemoveInvalidCharacters(string content, char replace = '_', bool doNotReplaceBackslashes = false)
{
if (string.IsNullOrEmpty(content))
return content;
var idx = content.IndexOfAny(InvalidCharacters);
if (idx >= 0)
{
var sb = new StringBuilder(content);
while (idx >= 0)
{
if (sb[idx] != '\\' || !doNotReplaceBackslashes)
sb[idx] = replace;
idx = content.IndexOfAny(InvalidCharacters, idx+1);
}
return sb.ToString();
}
return content;
}
메서드가 다음 기간 동안 "있는 그대로" 컴파일하지 않습니다.InvalidCharacters
합니다.
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();
}
}
다음은 파일 이름의 모든 잘못된 문자를 대체 문자로 대체하는 함수입니다.
public static string ReplaceIllegalFileChars(string FileNameWithoutPath, char ReplacementChar)
{
const string IllegalFileChars = "*?/\\:<>|\"";
StringBuilder sb = new StringBuilder(FileNameWithoutPath.Length);
char c;
for (int i = 0; i < FileNameWithoutPath.Length; i++)
{
c = FileNameWithoutPath[i];
if (IllegalFileChars.IndexOf(c) >= 0)
{
c = ReplacementChar;
}
sb.Append(c);
}
return (sb.ToString());
}
예를 들어 밑줄을 대체 문자로 사용할 수 있습니다.
NewFileName = ReplaceIllegalFileChars(FileName, '_');
아니면 그냥 할 수도 있습니다.
[YOUR STRING].Replace('\\', ' ').Replace('/', ' ').Replace('"', ' ').Replace('*', ' ').Replace(':', ' ').Replace('?', ' ').Replace('<', ' ').Replace('>', ' ').Replace('|', ' ').Trim();
언급URL : https://stackoverflow.com/questions/146134/how-to-remove-illegal-characters-from-path-and-filenames
'programing' 카테고리의 다른 글
Angular 2+에서 spec.ts 파일 없이 구성 요소를 생성하는 중 (0) | 2023.05.18 |
---|---|
Azure Service Fabric에서 상태 저장 서비스를 사용해야 하는 시기와 외부 지속성에 의존해야 하는 시기 이해 (0) | 2023.05.18 |
코드사인이 키 체인의 키 "액세스"에 액세스하고 싶어합니다. 로그인 암호를 입력했지만 계속 묻습니다. (0) | 2023.05.18 |
Windows 명령줄에서 Git Bash를 시작하는 방법 (0) | 2023.05.18 |
module.exports 내의 "local" 함수를 module.exports 내의 다른 함수에서 호출하시겠습니까? (0) | 2023.05.18 |