Как удалить незаконные символы из Пути и имен файлов?
Мне нужен надежный и простой способ удаления незаконных символов пути и файла из простой строки. Я использовал приведенный ниже код, но он, кажется, ничего не делает, что я упускаю?
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();
}
}
}
25 ответов:
вместо этого попробуйте что-то вроде этого;
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(), ""); }
но я должен согласиться с комментариями, я бы, вероятно, попытался разобраться с источником незаконных путей, а не пытаться исказить незаконный путь в законный, но, вероятно, непреднамеренный.
Edit: или потенциально "лучшее" решение, используя регулярное выражение.
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, чтобы вы могли проверить метод.
public string GetSafeFilename(string filename) { return string.Join("_", filename.Split(Path.GetInvalidFileNameChars())); }
этот ответ был на другой поток Цереры, мне очень нравится аккуратный и простой.
вы можете удалить незаконные символы с помощью Linq следующим образом:
var invalidChars = Path.GetInvalidFileNameChars(); var invalidCharsRemoved = stringWithInvalidChars .Where(x => !invalidChars.Contains(x)) .ToArray();
EDIT
Вот как это выглядит с необходимым редактированием, упомянутым в комментариях: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, а также кавычки ("), меньше (), pipe (|), backspace (\b), null (\0) и tab (\t).
Это не лучше, с
Path.GetInvalidPathChars
метод. Он содержит точно такое же замечание.
для начала Trim удаляет только символы из начала или конца строки. Во-вторых, вы должны оценить, действительно ли вы хотите удалить оскорбительные символы или быстро потерпеть неудачу и сообщить пользователю, что их имя файла недействительно. Мой выбор-последнее, но мой ответ должен по крайней мере показать вам, как делать все правильно и неправильно:
и StackOverflow вопрос, показывающий, как проверить, если данная строка является допустимым именем файла. Примечание Вы можете использовать регулярное выражение из этого вопроса для удаления символов с заменой регулярного выражения (Если вам действительно нужно это сделать).
файлов с именами:
string cleanFileName = String.Join("", fileName.Split(Path.GetInvalidFileNameChars()));
для полного пути:
string cleanPath = String.Join("", path.Split(Path.GetInvalidPathChars()));
Я использую регулярные выражения для достижения этой цели. Во-первых, я динамически строю регулярное выражение.
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);
улучшение заключается только в том, чтобы избежать автоматически генерируемого регулярного выражения.
лучший способ удалить незаконный символ из пользовательского ввода-заменить незаконный символ с помощью класса 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">
вот фрагмент кода, который должен помочь для .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, ""); } }
большинство решений выше объединяют незаконные символы как для пути, так и для имени файла, что неверно (даже если оба вызова в настоящее время возвращают один и тот же набор символов). Я бы сначала разделил путь+имя файла на путь и имя файла, а затем применил соответствующий набор к любому из них, а затем снова объединил их.
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/RegExpPSD12062005021717AM/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()); }
сканирование ответов здесь, все они**, похоже, связаны с использованием массива символов недопустимого имени файла.
конечно, это может быть микрооптимизация-но в интересах всех, кто может искать, чтобы проверить большое количество значений для допустимых имен файлов, стоит отметить, что создание хэш - набора недопустимых символов приведет к значительно лучшей производительности.
Я был очень удивлен (шокирован) в прошлом, как быстро hashset (или словарь) превосходит итерацию по списку. Со строками, это смехотворно малое число (около 5-7 элементов из памяти). С большинством других простых данных (ссылки на объекты, числа и т. д.) волшебный кроссовер, похоже, составляет около 20 элементов.
есть 40 недопустимых символов в пути.InvalidFileNameChars "список". Сделал поиск сегодня, и здесь есть довольно хороший тест на 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 ?? ''; 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 != '') 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; } }
вы можете использовать метод ясно.
public static bool IsValidFilename(string testName) { return !new Regex("[" + Regex.Escape(new String(System.IO.Path.GetInvalidFileNameChars())) + "]").IsMatch(testName); }
имя файла не может содержать символы
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
заменяет все неверные данные на_
.
Это то, чего вы хотите, и избежать столкновения
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 не включает все недопустимые символы, возможные с кодами ascii от 0 до 255. посмотреть здесь и MSDN
- возможность определить символ замены
источник:
public static class FileNameCorrector { private static HashSet<char> invalid = new HashSet<char>(Path.GetInvalidFileNameChars()); public static string ToValidFileName(this string name, char replacement = '') { var builder = new StringBuilder(); foreach (var cur in name) { if (cur > 31 && cur < 128 && !invalid.Contains(cur)) { builder.Append(cur); } else if (replacement != '') { builder.Append(replacement); } } return builder.ToString(); } }