Как удалить недопустимые символы из путей и имен файлов?
Мне нужен надежный и простой способ удаления незаконных путей и файлов из простой строки. Я использовал приведенный ниже код, но он ничего не делает, что мне не хватает?
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();
}
}
}
Ответы
Ответ 1
Попробуйте что-то вроде этого:
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's.
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, "");
Тем не менее, возникает вопрос, почему вы делаете это в первую очередь.
Ответ 2
Оригинальный вопрос, заданный для "удаления недопустимых символов":
public string RemoveInvalidChars(string filename)
{
return string.Concat(filename.Split(Path.GetInvalidFileNameChars()));
}
Вместо этого вы можете заменить их:
public string ReplaceInvalidChars(string filename)
{
return string.Join("_", filename.Split(Path.GetInvalidFileNameChars()));
}
Этот ответ был на другой ветке от Цереры, мне очень нравится это аккуратно и просто.
Ответ 3
Я использую Linq для очистки имен файлов. Вы можете легко расширить это, чтобы проверить допустимые пути.
private static string CleanFileName(string fileName)
{
return Path.GetInvalidFileNameChars().Aggregate(fileName, (current, c) => current.Replace(c.ToString(), string.Empty));
}
Update
Некоторые комментарии указывают, что этот метод не работает для них, поэтому я включил ссылку на фрагмент DotNetFiddle, чтобы вы могли проверить метод.
https://dotnetfiddle.net/nw1SWY
Ответ 4
Вы можете удалить незаконные символы, используя 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());
Ответ 5
Все это отличные решения, но все они полагаются на Path.GetInvalidFileNameChars
, которые могут быть не такими надежными, как вы думаете. Обратите внимание на следующее примечание в документации MSDN на Path.GetInvalidFileNameChars
:
Массив, возвращаемый с помощью этого метода, не гарантированно содержит полный набор символов, которые недействительны в именах файлов и каталогов. Полный набор недопустимых символов может отличаться в зависимости от файловой системы. Например, на платформах для настольных компьютеров Windows недопустимые символы пути могут включать символы ASCII/Unicode с 1 по 31, а также цитату ("), меньше (<), больше ( > ), pipe (|), backspace (\ b), null (\ 0) и tab (\ t).
Это не лучше с Path.GetInvalidPathChars
. Он содержит то же самое замечание.
Ответ 6
Для имен файлов:
string cleanFileName = String.Join("", fileName.Split(Path.GetInvalidFileNameChars()));
Для полных путей:
string cleanPath = String.Join("", path.Split(Path.GetInvalidPathChars()));
Обратите внимание, что если вы намереваетесь использовать это в качестве функции безопасности, более надежным подходом будет расширение всех путей, а затем проверка того, что указанный пользователем путь действительно является дочерним для каталога, к которому у пользователя должен быть доступ.
Ответ 7
Для начала Trim только удаляет символы с начала или конца строки. Во-вторых, вы должны оценить, действительно ли хотите удалить оскорбительные символы или быстро выйти из строя и сообщить пользователю, что их имя недействительно. Мой выбор - последний, но мой ответ должен хотя бы показать вам, как правильно поступать правильно и неправильно:
fooobar.com/questions/10286/.... Обратите внимание, что вы можете использовать регулярное выражение из этого вопроса для удаления символов с заменой регулярного выражения (если вам действительно нужно это сделать).
Ответ 8
Я использую регулярные выражения для достижения этого. Во-первых, я динамически создаю регулярное выражение.
string regex = string.Format(
"[{0}]",
Regex.Escape(new string(Path.GetInvalidFileNameChars())));
Regex removeInvalidChars = new Regex(regex, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant);
Затем я просто вызываю removeInvalidChars.Replace, чтобы найти и заменить. Это, очевидно, может быть расширено, чтобы покрывать также символы пути.
Ответ 9
Лучшим способом удаления недопустимого символа с пользовательского ввода является замена нелегального символа с помощью класса 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">
Ответ 10
Я абсолютно предпочитаю идею Джеффа Йетса. Он будет работать отлично, если вы слегка измените его:
string regex = String.Format("[{0}]", Regex.Escape(new string(Path.GetInvalidFileNameChars())));
Regex removeInvalidChars = new Regex(regex, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant);
Улучшение заключается лишь в том, чтобы избежать автоматического создания регулярного выражения.
Ответ 11
Вот фрагмент кода, который должен помочь для .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, "");
}
}
Ответ 12
В большинстве решений выше сочетаются незаконные символы для обоих путей и имени файла, что неверно (даже если оба вызова в настоящее время возвращают один и тот же набор символов). Сначала я разделил бы путь + имя файла в пути и имя файла, затем применил бы соответствующий набор к ним, если они есть, а затем снова объединить два.
wvd_vegt
Ответ 13
Если вы удалите или замените одним символом недопустимые символы, вы можете столкнуться с ошибками:
<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
Ответ 14
Выбросьте исключение.
if ( fileName.IndexOfAny(Path.GetInvalidFileNameChars()) > -1 )
{
throw new ArgumentException();
}
Ответ 15
Я написал это чудовище для удовольствия, он позволяет вам совершать кругооборот:
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;
}
}
Ответ 16
Я думаю, что гораздо легче проверить использование регулярного выражения и указать, какие символы разрешены, вместо того, чтобы пытаться проверить все плохие символы.
См. Следующие ссылки:
http://www.c-sharpcorner.com/UploadFile/prasad_1/RegExpPSD12062005021717AM/RegExpPSD.aspx
http://www.windowsdevcenter.com/pub/a/oreilly/windows/news/csharp_0101.html
Кроме того, выполните поиск в редакторе регулярных выражений, они многое помогут. Есть некоторые, вокруг которых даже вывести код в С# для вас.
Ответ 17
Кажется, что это 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());
}
Ответ 18
Сканирование по ответам здесь, все они **, похоже, связаны с использованием массива char недопустимых имен файлов.
Разумеется, это может быть микрооптимизация - но для тех, кто мог бы проверить большое количество значений для действительных имен файлов, стоит отметить, что создание хешета недопустимых символов приведет к значительно лучшей производительности,
В прошлом я был очень удивлен (потрясен), насколько быстро хешсет (или словарь) превосходит итерирование по списку. Со строками это смехотворно низкое число (около 5-7 предметов из памяти). С большинством других простых данных (ссылки на объекты, числа и т.д.) Магический кроссовер, кажется, составляет около 20 элементов.
В списке Path.InvalidFileNameChars имеется 40 недопустимых символов. Был ли поиск сегодня и там довольно хороший тест здесь, на StackOverflow, который показывает, что hashset займет чуть более половины времени массива/списка для 40 элементов: fooobar.com/questions/23329/...
Здесь класс-помощник, который я использую для дезинфекции путей. Я забыл, почему у меня появился вариант замены, но он там как милый бонус.
Дополнительный бонусный метод "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;
}
}
Ответ 19
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;
}
}
Вы можете использовать метод четко.
Ответ 20
Имя файла не может содержать символы из символов 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
заменяет все неверные данные на _
.
Ответ 21
Один лайнер для очистки строки из любых незаконных символов для имен файлов 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, "");
Ответ 22
public static bool IsValidFilename(string testName)
{
return !new Regex("[" + Regex.Escape(new String(System.IO.Path.GetInvalidFileNameChars())) + "]").IsMatch(testName);
}
Ответ 23
Это позволит вам хотеть и избегать столкновений
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();
}
Ответ 24
Я думаю, что вопрос уже не полный ответил...
Ответы описывают только чистое имя файла ИЛИ путь... не оба. Вот мое решение:
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;
}
Ответ 25
Я создал метод расширения, который объединяет несколько предложений:
- Удержание незаконных символов в хэш-наборе
- Фильтрация символов ниже ascii 127. Поскольку Path.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 = '\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();
}
}
Ответ 26
Или вы можете просто сделать
[YOUR STRING].Replace('\\', ' ').Replace('/', ' ').Replace('"', ' ').Replace('*', ' ').Replace(':', ' ').Replace('?', ' ').Replace('<', ' ').Replace('>', ' ').Replace('|', ' ').Trim();