Як видалити незаконні символи із шляху та імен файлів?


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
Обрізка видаляє символи з початку та кінця рядків. Однак, напевно, слід запитати, чому дані недійсні, і замість того, щоб спробувати і очистити / виправити дані, відхилити дані.
user7116

8
Назви стилів Unix не дійсні для Windows, і я не хочу мати справу з 8.3 короткими іменами.
Gary Willoughby

GetInvalidFileNameChars()зніме такі речі, як: \ etc із шляхів до папок.
CAD блокується

1
Path.GetInvalidPathChars()не здається позбавити *або?
CAD блокується

18
Я перевірив п'ять відповідей з цього питання (тимчасовий цикл 100 000) і наступний метод є найшвидшим. Регулярний вираз зайняв 2-е місце і був на 25% повільнішим: публічний рядок GetSafeFilename (ім'я файла рядка) {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.

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
Не потрібно додавати два списки разом. Список незаконних імен файлів char містить список незаконних знаків char та містить ще декілька. Ось списки обох списків, поданих до 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,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
Бота

9
@sjbotha це може бути правдою для Windows та впровадження Microsoft .NET. Я не бажаю робити те саме припущення, як, наприклад, для моно-операційного Linux.
Меттью Шарлі

7
Щодо першого рішення. Чи не повинен StringBuilder бути більш ефективним, ніж призначення рядків?
epignosisx

6
Для чого варто, @MatthewScharley, монореалізація GetInvalidPathChars () повертає лише 0x00, а GetInvalidFileNameChars () повертає лише 0x00 та '/' під час роботи на платформах, які не є Windows. У Windows списки недійсних символів значно довші, а GetInvalidPathChars () повністю дублюється всередині GetInvalidFileNameChars (). Це не зміниться в найближчому майбутньому, тому все, що ви дійсно робите, це подвоєння часу, яке ця функція потребує для запуску, оскільки ви переживаєте, що визначення дійсного шляху зміниться незабаром. Якого не буде.
Воррен Румак

13
@Charleh ця дискусія настільки непотрібна ... код завжди слід оптимізувати, і немає ризику, щоб це було невірно. Ім'я файлу також є частиною шляху. Так що це просто нелогічно, що GetInvalidPathChars()може містити символи, які GetInvalidFileNameChars()цього не мали б. Ви не приймаєте правильності щодо "передчасної" оптимізації. Ви просто використовуєте неправильний код.
Стефан Фабіян

353

В оригінальному запитанні було запропоновано "видалити незаконних символів":

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

36
Я перевірив п’ять методів із цього питання (тимчасовий цикл 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, це не працює, повертається початковий рядок.
Джон

Ви дійсно можете зробити це з допомогою Linq , як це , хоча: var invalid = new HashSet<char>(Path.GetInvalidPathChars()); return new string(originalString.Where(s => !invalid.Contains(s)).ToArray()). Продуктивність, мабуть, не велика, але це, мабуть, не має значення.
Кейсі

2
@Karan або Jon Який вклад ви надсилаєте цю функцію? Дивіться мою редакцію для перевірки цього методу.
Майкл Мінтон

3
Це легко - хлопці передавали рядки з дійсними символами. Запропоновано прохолодний розчин агрегату.
Нікмайович

89

Ви можете видалити незаконні символи за допомогою 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());

1
Мені подобається такий спосіб: ви зберігаєте лише дозволені знаки в рядку (що є не що інше, як масив char).
Чувак Паскалу

6
Я знаю, що це старе питання, але це надзвичайна відповідь. Однак я хотів додати, що в c # ви не можете передавати з char [] на рядок ні неявно, ні явно (божевільно, я знаю), тому вам потрібно буде впустити його в конструктор рядків.
JNYRanger

1
Я цього не підтвердив, але сподіваюсь, що Path.GetInvalidPathChars () буде набором GetInvalidFileNameChars () і охоплює імена файлів, і шляхи, тому я, ймовірно, використовую це.
angularsen

3
@anjdreas насправді Path.GetInvalidPathChars (), схоже, є підмножиною Path.GetInvalidFileNameChars (), а не навпаки. Наприклад, Path.GetInvalidPathChars () не поверне "?"
Рафаель Коста

1
Це хороша відповідь. Я використовую як список імен файлів, так і список файлових маршрутів: ____________________________ рядок cleanData = новий рядок (data.Where (x =>! Path.GetInvalidFileNameChars (). Містить (x) &&! Path.GetInvalidPathChars (). Містить (x)). ToArray ());
goamn

27

Це все чудові рішення, але всі вони покладаються на Path.GetInvalidFileNameChars, які можуть бути не настільки надійними, як ви могли б подумати. Зверніть увагу на таке зауваження в документації MSDN щодо Path.GetInvalidFileNameChars:

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

З Path.GetInvalidPathCharsметодом це не краще . Він містить точно таке зауваження.


13
Тоді в чому сенс Path.GetInvalidFileNameChars? Я б очікував, що він поверне саме недійсні символи для поточної системи, покладаючись на .NET, щоб знати, у якій файловій системі я працюю, і представив мені відповідні недійсні символи. Якщо це не так, і він просто повертає твердо кодовані символи, які в першу чергу не є надійними, цей метод слід видалити, оскільки він має нульове значення.
Jan

1
Я знаю, що це старий коментар, але, @Jan, ти можеш хотіти написати в іншій файловій системі, можливо, саме тому є попередження.
fantastik78

3
@ fantastik78 хороший момент, але в цьому випадку я хотів би мати додатковий аргумент перерахунку, щоб вказати мій віддалений FS. Якщо це занадто великі зусилля з обслуговування (що, швидше за все, має місце), весь цей метод все-таки є поганою ідеєю, оскільки він створює неправильне враження про безпеку.
січня

1
@Jan Я повністю згоден з тобою, я просто сперечався про попередження.
fantastik78

Цікаво, що це свого роду "чорний список" недійсних символів. Хіба не було б краще "додати до списку" лише відомі дійсні символи тут ?! Нагадує мені про дурну ідею "вірус-сканер" замість дозволених додатків ....
Бернхард

26

Імена файлів:

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

Для повних шляхів:

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

Зауважте, що якщо ви маєте намір використовувати це як функцію захисту, більш надійним підходом буде розширення всіх шляхів, а потім перевірка того, що шлях, що надається користувачем, є дійсно дочірнім каталогом, до якого повинен мати доступ користувач.


18

Для початку Trim видаляє символи лише з початку або в кінці рядка . По-друге, слід оцінити, чи дійсно ви хочете видалити образливі символи, чи швидко вийти з ладу, і повідомити користувачеві, що ім’я файлу недійсне. Мій вибір - останній, але моя відповідь повинна принаймні показати вам, як робити справи правильно І неправильно:

Питання StackOverflow, яке показує, як перевірити, чи дана рядок є дійсним ім'ям файлу . Зауважте, що ви можете скористатись регулярним виразом із цього запитання, щоб видалити символи з регулярною заміною виразу (якщо вам це потрібно зробити).


Я особливо погоджуюся з другою порадою.
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">

5
IMHO це рішення набагато краще, ніж інші Замість пошуку всіх недійсних символів просто визначте, які є дійсними.
ігоруші

15

Для цього я використовую регулярні вирази. По-перше, я динамічно будую регулярний вираз.

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

Тоді я просто закликаю deleteInvalidChars.Replace, щоб знайти і замінити. Очевидно, це може бути розширено і для покриття символів шляху.


Як не дивно, це працює на мене. Я двічі перевіряю це, коли отримаю шанс. Чи можете ви бути більш конкретними та пояснити, що саме для вас не працює?
Джефф Йейтс

1
Це не спрацює (принаймні належним чином), оскільки ви не уникнете належних символів, а деякі з них мають особливе значення. Зверніться до моєї відповіді, як це зробити.
Меттью Шарлі

@Jeff: Ваша версія все ще краща, ніж Метью, якщо ви трохи її змінили. Дивіться мою відповідь про те, як.
Jan

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);
регекс

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


+1: Дуже вірно. Сьогодні, працюючи в .NET 4.0, рішення регулярного виразів з верхньої відповіді наніс усі зворотні риси повним шляхом. Тому я зробив регулярний вираз для шляху dir і регулярний вираз для лише імені файлу, очищений окремо і рекомбінований
dario_ramos

Це може бути правдою, але це не відповідає на питання. Я не впевнений, що розпливчастий «Я б це зробив так» є надзвичайно корисним у порівнянні з деякими повноцінними рішеннями, які вже є тут (див., Наприклад, відповідь Ліллі, нижче)
Ян Грінґер

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

5

Киньте виняток.

if ( fileName.IndexOfAny(Path.GetInvalidFileNameChars()) > -1 )
            {
                throw new ArgumentException();
            }

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 () найкраще, оскільки він буде містити знання про те, що є чи ні " t дійсна у файловій системі, на якій працює ваша програма. Навіть якщо ваша програма ніколи не працюватиме в 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), коли ви використовуєте функцію "Будь-яка".
II СТРІЛКИ

@IIARROWS і що це на ваш погляд?
Олексій F

Я не знаю, це просто не відчувалось так, коли я писав свій коментар ... тепер, коли я намагався обчислити це, схоже, ти маєш рацію.
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;
        }
    }

Ви можете використовувати метод чітко.


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

1
public static bool IsValidFilename(string testName)
{
    return !new Regex("[" + Regex.Escape(new String(System.IO.Path.GetInvalidFileNameChars())) + "]").IsMatch(testName);
}

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

    }

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;
}

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

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();
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.