Как удалить недопустимые символы из пути и имен файлов?

456

Мне нужен надежный и простой способ удалить недопустимые пути и символы файла из простой строки. Я использовал приведенный ниже код, но он, похоже, ничего не делает, что мне не хватает?

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
Trim удаляет символы из начала и конца строк. Тем не менее, вы, вероятно, должны спросить, почему данные являются недействительными, и вместо того, чтобы пытаться очистить / исправить данные, отклоните данные.
user7116 28.09.08
8
Имена стилей Unix недопустимы в Windows, и я не хочу иметь дело с 8.3 короткими именами.
Гэри Уиллоуби
GetInvalidFileNameChars()удалит такие вещи как: \ etc из путей к папкам.
CAD-блок
1
Path.GetInvalidPathChars()кажется, не раздеться *или?
CAD парень
19
Я проверил пять ответов на этот вопрос (временной цикл 100 000), и следующий метод самый быстрый. Регулярное выражение заняло 2-е место и было на 25% медленнее: общедоступная строка GetSafeFilename (string filename) {return string.Join ("_", filename.Split (Path.GetInvalidFileNameChars ())); }
Brain2000

Ответы:

494

Попробуйте что-то вроде этого вместо этого;

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, "");

Тем не менее, возникает вопрос, почему вы делаете это в первую очередь.

Мэтью Шарли
источник
40
Нет необходимости добавлять два списка вместе. Список символов недопустимого имени файла содержит список символов недопустимого пути и содержит еще несколько. Вот списки обоих списков, приведенных к int: 34,60,62,124,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,58,42,63,92,47 34,60,62,124,0,2 , 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27 , 28,29,30,31
Бота,
9
@sjbotha это может быть правдой в Windows и реализации Microsoft .NET Я не хочу делать то же самое предположение для, скажем, моно под управлением Linux.
Мэтью Шарли
7
По поводу первого решения. Разве StringBuilder не должен быть более эффективным, чем строковые назначения?
эпигнозокс
6
Для чего стоит @MatthewScharley, реализация MonIn для GetInvalidPathChars () возвращает только 0x00, а GetInvalidFileNameChars () возвращает только 0x00 и '/' при работе на платформах, отличных от Windows. В Windows списки недопустимых символов намного длиннее, и GetInvalidPathChars () полностью дублируется внутри GetInvalidFileNameChars (). Это не изменится в обозримом будущем, поэтому все, что вы на самом деле делаете, это удваивает время, необходимое для выполнения этой функции, потому что вы беспокоитесь о том, что определение действительного пути скоро изменится. Что это не так.
Уоррен Румак
13
@ Чарлех, это обсуждение так ненужно ... код всегда должен быть оптимизирован, и нет риска, что это будет неправильно. Имя файла тоже является частью пути. Так что просто нелогично, если GetInvalidPathChars()бы в нем содержались символы, GetInvalidFileNameChars()которых не было бы. Вы не принимаете правильность над «преждевременной» оптимизацией. Вы просто используете плохой код.
Стефан Фабиан
355

Оригинальный вопрос, заданный для «удаления недопустимых символов»:

public string RemoveInvalidChars(string filename)
{
    return string.Concat(filename.Split(Path.GetInvalidFileNameChars()));
}

Вместо этого вы можете заменить их:

public string ReplaceInvalidChars(string filename)
{
    return string.Join("_", filename.Split(Path.GetInvalidFileNameChars()));    
}

Этот ответ был на другой ветке от Цереры , мне очень нравится это аккуратно и просто.

Шехаб Фаузи
источник
10
Чтобы точно ответить на вопрос ОП, вам нужно будет использовать «» вместо «_», но ваш ответ, вероятно, относится к большинству из нас на практике. Я думаю, что замена нелегальных символов более легальными делается чаще.
BH
37
Я проверил пять методов из этого вопроса (временной цикл 100 000), и этот метод самый быстрый. Регулярное выражение заняло 2-е место и было на 25% медленнее, чем этот метод.
Brain2000
10
Чтобы обратиться к комментарию @BH, можно просто использовать string.Concat (name.Split (Path.GetInvalidFileNameChars ()))
Майкл Саттон
210

Я использую 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

Майкл Минтон
источник
4
Это не сработало для меня. Метод не возвращает чистую строку. Он возвращает переданное имя файла как есть.
Каран
Что @Karan сказал, это не работает, оригинальная строка возвращается.
Jon
Вы действительно можете сделать это с помощью Linq , как это , хотя: var invalid = new HashSet<char>(Path.GetInvalidPathChars()); return new string(originalString.Where(s => !invalid.Contains(s)).ToArray()). Производительность, вероятно, не велика, но это, вероятно, не имеет значения.
Кейси
2
@ Каран или Джон Какой вклад вы посылаете этой функции? Смотрите мои правки для проверки этого метода.
Майкл Минтон
3
Это просто - парни передавали строки с действительными символами. Проголосовал за прохладный агрегатный раствор.
Никмаович
89

Вы можете удалить незаконные символы с помощью 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());
Грегор Славец
источник
1
Мне нравится этот способ: вы сохраняете только разрешенные символы в строке (это не что иное, как массив символов).
Чувак Паскало
6
Я знаю, что это старый вопрос, но это потрясающий ответ. Тем не менее, я хотел добавить, что в c # вы не можете приводить из char [] к строковым значениям либо неявно, либо явно (без ума, я знаю), поэтому вам нужно поместить его в строковый конструктор.
JNYRanger
1
Я не подтвердил это, но я ожидаю, что Path.GetInvalidPathChars () будет надмножеством GetInvalidFileNameChars () и будет охватывать как имена файлов, так и пути, поэтому я, вероятно, использовал бы это вместо этого.
angularsen
3
@anjdreas на самом деле Path.GetInvalidPathChars () представляется подмножеством Path.GetInvalidFileNameChars (), а не наоборот. Например, Path.GetInvalidPathChars () не будет возвращать «?».
Рафаэль Коста
1
Это хороший ответ. Я использую как список имен файлов, так и список путей к файлам: ____________________________ строка cleanData = новая строка (data.Where (x =>! Path.GetInvalidFileNameChars (). Contains (x) &&! Path.GetInvalidPathChars (). Contains (x)). ToArray ());
Goamn
27

Все это отличные решения, но они все полагаются Path.GetInvalidFileNameChars, что может быть не так надежно, как вы думаете. Обратите внимание на следующее замечание в документации MSDN Path.GetInvalidFileNameChars:

Массив, возвращаемый этим методом, не обязательно содержит полный набор символов, которые недопустимы в именах файлов и каталогов. Полный набор недопустимых символов может варьироваться в зависимости от файловой системы. Например, на настольных платформах под управлением Windows недопустимые символы пути могут включать символы ASCII / Unicode от 1 до 31, а также кавычки ("), меньше чем (<), больше чем (>), pipe (|), backspace ( \ b), ноль (\ 0) и табуляция (\ t).

Это не лучше с Path.GetInvalidPathCharsметодом. Он содержит точно такое же замечание.

Рене
источник
13
Тогда в чем смысл Path.GetInvalidFileNameChars? Я ожидаю, что он вернет точно недопустимые символы для текущей системы, полагаясь на .NET, чтобы узнать, на какой файловой системе я работаю, и предоставив мне подходящие неверные символы. Если это не так, и он просто возвращает жестко закодированные символы, которые в первую очередь не надежны, этот метод следует удалить, поскольку он имеет нулевое значение.
Jan
1
Я знаю, что это старый комментарий, но @Jan вы можете написать в другой файловой системе, может быть, поэтому есть предупреждение.
fantastik78
3
@ fantastik78 хороший момент, но в этом случае я хотел бы иметь дополнительный аргумент enum для указания моей удаленной FS. Если это слишком много усилий по обслуживанию (что, скорее всего, так), весь этот метод все еще плохая идея, потому что он дает неправильное представление о безопасности.
января
1
@ Ян, я полностью согласен с тобой, я просто спорил о предупреждении.
fantastik78
Интересно, что это своего рода «черный список» недопустимых символов. Не лучше ли «внести в белый список» только известные действительные символы здесь ?! Напоминает мне глупую идею "virusscanner" вместо разрешенных приложений из белого списка ....
Бернхард
26

Для имен файлов:

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

Для полных путей:

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

Обратите внимание, что если вы намереваетесь использовать это как функцию безопасности, более надежный подход будет состоять в том, чтобы развернуть все пути и затем убедиться, что указанный пользователем путь действительно является дочерним для каталога, к которому у пользователя должен быть доступ.

Лили Финли
источник
18

Для начала, Trim удаляет только символы из начала или конца строки . Во-вторых, вы должны оценить, действительно ли вы хотите удалить оскорбительные символы или быстро потерпеть неудачу и сообщить пользователю, что их имя файла недействительно. Мой выбор - последний, но мой ответ должен, по крайней мере, показать вам, как делать вещи правильно И неправильно:

Вопрос StackOverflow, показывающий, как проверить, является ли данная строка допустимым именем файла . Обратите внимание, что вы можете использовать регулярное выражение из этого вопроса, чтобы удалить символы с заменой регулярного выражения (если вам действительно нужно это сделать).

user7116
источник
Я особенно согласен со вторым советом.
OregonGhost
4
Обычно я соглашусь со вторым, но у меня есть программа, которая генерирует имя файла и в некоторых ситуациях может содержать недопустимые символы. Поскольку моя программа генерирует недопустимые имена файлов, я считаю целесообразным удалить / заменить эти символы. (Просто указывает на действительный вариант использования)
JDB до сих пор помнит Монику
16

Лучший способ удалить недопустимый символ из пользовательского ввода - заменить недопустимый символ с помощью класса 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">
anomepani
источник
5
ИМХО, это решение намного лучше, чем другие. Вместо поиска всех недопустимых символов просто определите, какие из них действительны.
игоруши
15

Я использую регулярные выражения для достижения этой цели. Во-первых, я динамически строю регулярное выражение.

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

Затем я просто вызываю removeInvalidChars.Replace, чтобы найти и заменить. Это, очевидно, может быть расширено и для символов пути.

Джефф Йейтс
источник
Странно, это работает на меня. Я проверю это дважды, когда получу шанс. Можете ли вы быть более конкретным и объяснить, что именно не работает для вас?
Джефф Йейтс
1
Это не будет работать (по крайней мере, правильно), потому что вы не экранируете символы пути правильно, а некоторые из них имеют особое значение. Обратитесь к моему ответу, как это сделать.
Мэтью Шарли
@Jeff: Ваша версия все еще лучше, чем у Мэтью, если вы немного измените ее. Обратитесь к моему ответу о том, как.
января
2
Я также добавил бы некоторые другие недопустимые шаблоны имен файлов, которые можно найти в MSDN, и расширил бы ваше решение до следующего регулярного выражения:new Regex(String.Format("^(CON|PRN|AUX|NUL|CLOCK\$|COM[1-9]|LPT[1-9])(?=\..|$)|(^(\.+|\s+)$)|((\.+|\s+)$)|([{0}])", Regex.Escape(new String(Path.GetInvalidFileNameChars()))), RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.CultureInvariant);
yar_shukan
13

Я абсолютно предпочитаю идею Джеффа Йейтса. Это будет работать отлично, если вы немного измените его:

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, "");
    }
}
Джеймс
источник
8

Большинство решений выше объединяют недопустимые символы как для пути, так и для имени файла, что неверно (даже если оба вызова в настоящее время возвращают один и тот же набор символов). Сначала я бы разделил путь + имя файла на путь и имя файла, затем применил соответствующий набор к любому из них, а затем снова объединил их.

wvd_vegt

wvd_vegt
источник
+1: очень верно. Сегодня, работая в .NET 4.0, решение regex из верхнего ответа уничтожило все обратные косые черты в полном пути. Поэтому я сделал регулярное выражение для пути dir и регулярное выражение только для имени файла, которое было очищено и
повторно объединено
Это может быть правдой, но это не отвечает на вопрос. Я не уверен, что расплывчатое «я бы сделал это так» ужасно полезно по сравнению с некоторыми из полных решений, которые уже здесь (см., Например, ответ Лилли, ниже)
Ян Грейнджер
6

Если вы удалите или замените одним символом недопустимые символы, вы можете столкнуться:

<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
Maxence
источник
5

Брось исключение.

if ( fileName.IndexOfAny(Path.GetInvalidFileNameChars()) > -1 )
            {
                throw new ArgumentException();
            }
mirezus
источник
4

Я написал этого монстра для забавы, он позволяет вам туда и обратно:

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;
    }
}
Йохан Ларссон
источник
1
Мне это нравится, потому что это позволяет избежать двух разных строк, создающих один и тот же путь.
Ким
3

Я думаю, что гораздо проще проверить, используя регулярные выражения и указав, какие символы разрешены, вместо того, чтобы пытаться проверить все плохие символы. Смотрите эти ссылки: http://www.c-sharpcorner.com/UploadFile/prasad_1/RegExpPSD12062005021717AM/RegExpPSD.aspx http://www.windowsdevcenter.com/pub/a/oreilly/windows/news/csharp_0101.html

Кроме того, выполните поиск «редакторов регулярных выражений», они очень помогают. Есть некоторые, которые даже выводят код на C # для вас.

Сандор Давидхази
источник
Учитывая, что .net - это фреймворк, предназначенный для запуска программ на нескольких платформах (например, Linux / Unix, а также Windows), я считаю, что Path.GetInvalidFileNameChars () лучше, поскольку он будет содержать сведения о том, что есть или нет. Не действует в файловой системе, в которой выполняется ваша программа. Даже если ваша программа никогда не будет работать в Linux (возможно, она полна кода WPF), всегда есть вероятность, что какая-то новая файловая система Windows появится в будущем и будет иметь другие допустимые / недействительные символы. Свернуть свое собственное с помощью регулярных выражений - это заново изобрести колесо и перенести проблему с платформой в ваш собственный код.
Даниэль Скотт
Я согласен с вашим советом по редакторам / тестерам онлайн-регулярных выражений. Я нахожу их бесценными (поскольку регулярные выражения - это хитрые вещи, полные тонкости, которые могут легко сбить вас с толку, давая вам регулярное выражение, которое ведет себя каким-то дико неожиданным образом с крайними случаями). Мой фаворит - regex101.com (мне нравится, как он разбивает регулярное выражение и четко показывает, что он должен соответствовать). Мне также очень нравится debuggex.com, так как он имеет компактное визуальное представление групп совпадений и классов символов и еще много чего.
Даниэль Скотт
3

Это, кажется, 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());
    }
Алексей Ф
источник
1
Я не думаю, что это O (n), когда вы используете функцию «Any».
II СТРЕЛКИ
@IIARROWS и что это на твой взгляд?
Алексей Ф
Я не знаю, просто я так не чувствовал, когда писал свой комментарий ... теперь, когда я попытался его подсчитать, похоже, ты прав.
II СТРЕЛКИ
Я выбрал этот из-за вашего рассмотрения производительности. Спасибо.
Беренд Энгельбрехт
3

Просматривая ответы здесь, все они **, похоже, используют массив символов недопустимых символов имени файла.

Конечно, это может быть микрооптимизацией, но для тех, кто хочет проверить большое количество значений на предмет допустимости имен файлов, стоит отметить, что создание хэш-набора недопустимых символов приведет к заметному повышению производительности.

В прошлом я был очень удивлен (шокирован) тем, насколько быстро хешсет (или словарь) превосходит итерации по списку. Со строками это смехотворно мало (около 5-7 пунктов по памяти). С большинством других простых данных (ссылки на объекты, числа и т. Д.) Магический кроссовер, кажется, составляет около 20 предметов.

В списке «Path» .InvalidFileNameChars содержится 40 недопустимых символов. Сделал поиск сегодня, и здесь есть довольно хороший тест на 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;
    }
}
Дэниел Скотт
источник
2
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;
        }
    }

Вы можете использовать метод четко.

aemre
источник
2

Имя файла не может содержать символы из 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заменяет все неверные данные на _.

Спинки
источник
2

Один вкладыш для очистки строки от любых недопустимых символов для именования файлов 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, "");
Zananok
источник
1
public static bool IsValidFilename(string testName)
{
    return !new Regex("[" + Regex.Escape(new String(System.IO.Path.GetInvalidFileNameChars())) + "]").IsMatch(testName);
}
mbdavis
источник
0

Это будет хотеть, что вы хотите, и избежать столкновений

 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();

    }
mcintyre321
источник
0

Я думаю, что вопрос уже не полностью ответил ... Ответы описывают только чистое имя файла или путь ... не оба. Вот мое решение:

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;
}
Suplanus
источник
0

Я создал метод расширения, который объединяет несколько предложений:

  1. Хранение недопустимых символов в хэш-наборе
  2. Фильтрация символов ниже ascii 127. Так как Path.GetInvalidFileNameChars не включает все недопустимые символы, возможные с кодами ascii от 0 до 255. См. Здесь и 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();
    }
}
schoetbi
источник
0

Вот функция, которая заменяет все недопустимые символы в имени файла символом замены:

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, '_');
Ганс-Петер Калб
источник
В дополнение к ответу, который вы предоставили, рассмотрите краткое объяснение, почему и как это решает проблему.
jtate
-7

Или вы можете просто сделать

[YOUR STRING].Replace('\\', ' ').Replace('/', ' ').Replace('"', ' ').Replace('*', ' ').Replace(':', ' ').Replace('?', ' ').Replace('<', ' ').Replace('>', ' ').Replace('|', ' ').Trim();
Дэнни Фальяс
источник