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

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

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

person Gary Willoughby    schedule 28.09.2008    source источник
comment
Обрезка удаляет символы в начале и конце строк. Однако вам, вероятно, следует спросить, почему данные недействительны, и вместо того, чтобы пытаться очистить / исправить данные, отклоните данные.   -  person user7116    schedule 28.09.2008
comment
Имена в стиле Unix недействительны в Windows, и я не хочу иметь дело с короткими именами 8.3.   -  person Gary Willoughby    schedule 16.10.2009
comment
GetInvalidFileNameChars() удалит такие вещи, как: \ etc из путей к папкам.   -  person CAD bloke    schedule 20.05.2016
comment
Path.GetInvalidPathChars() не раздевается * или ?   -  person CAD bloke    schedule 20.05.2016
comment
Я проверил пять ответов на этот вопрос (временной цикл 100 000), и следующий метод является самым быстрым. Регулярное выражение заняло 2-е место и было на 25% медленнее: общедоступная строка GetSafeFilename (строка имени файла) {return string.Join (_, filename.Split (Path.GetInvalidFileNameChars ())); }   -  person Brain2000    schedule 15.07.2016
comment
Я добавил новую быструю альтернативу и несколько тестов в этом ответе   -  person c-chavez    schedule 29.09.2020


Ответы (29)


Вместо этого попробуйте что-нибудь подобное;

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

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

person Matthew Scharley    schedule 28.09.2008
comment
Я не знаю, стоит ли мне +1 к вашему ответу из-за такого неэффективного решения, которое оттолкнет пользователя от этого пути, или мне следует +1 к вашему ответу за то, что оно фактически отвечает на его вопрос! :) - person user7116; 28.09.2008
comment
@Michael Stum: они «компилируются» и должны быть своего рода конечным автоматом, но было бы наивно предполагать, что они гарантированно будут более эффективными под капотом, чем цикл. - person user7116; 28.09.2008
comment
На некоторой длине пути это, вероятно, не будет иметь большого значения. На более длинной строке, я полагаю, регулярное выражение будет быстрее. - person Matthew Scharley; 28.09.2008
comment
Я бы придерживался решения без регулярных выражений: в большинстве случаев оно, вероятно, будет более эффективным. Если вы используете решение с регулярным выражением, измените string.Format () только на [+ .... Если вы собираетесь рассматривать illegal как имя файла без пути после замены специальных символов, тогда вам понадобится только Path.InvalidFileNameChars (). - person Rory; 19.08.2010
comment
Нет необходимости складывать два списка вместе. Список символов недопустимого имени файла содержит список символов недопустимого пути и еще несколько. Вот списки обоих списков, приведенных к 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 - person Sarel Botha; 11.04.2011
comment
@sjbotha, это может быть правдой для Windows и реализации .NET от Microsoft. Я не хочу делать то же самое предположение, скажем, для моно-запуска Linux. - person Matthew Scharley; 17.04.2011
comment
По поводу первого решения. Разве StringBuilder не должен быть более эффективным, чем присвоение строк? - person epignosisx; 30.12.2011
comment
Если строка содержит китайские символы, решение может не сработать. - person PerlDev; 02.01.2012
comment
@PerlDev: Вы действительно это проверяли? characters должны быть многобайтовыми (sizeof(char) == 2), так что это не должно быть проблемой. Решение с регулярным выражением тоже должно быть в порядке. - person Matthew Scharley; 17.01.2012
comment
В чем проблема с дезинфекцией, Bob Tables? - person cregox; 09.11.2013
comment
Поправьте меня, если я ошибаюсь, но звонить и Path.GetInvalidFileNameChars(), и Path.GetInvalidPathChars() излишне. Одного Path.GetInvalidFileNameChars() должно быть достаточно. - person Joey Adams; 13.11.2013
comment
@JoeyAdams: см. Мой ответ Сарелу Боте. Короче говоря, одно является надмножеством другого в Windows. Лично я не желаю делать одинаковые ставки на кросс-платформу, а C # и .NET в целом все время получают все более широкую аудиторию через Mono. - person Matthew Scharley; 15.11.2013
comment
Как бы то ни было, @MatthewScharley, реализация GetInvalidPathChars () Mono возвращает только 0x00, а GetInvalidFileNameChars () возвращает только 0x00 и '/' при работе на платформах, отличных от Windows. В Windows списки недопустимых символов намного длиннее, а GetInvalidPathChars () полностью дублируется внутри GetInvalidFileNameChars (). Это не изменится в обозримом будущем, поэтому все, что вы на самом деле делаете, это удваивает время, необходимое для выполнения этой функции, потому что вы беспокоитесь, что определение допустимого пути когда-нибудь изменится. Чего не будет. - person Warren Rumak; 27.01.2014
comment
И давайте проясним это: эта часть исходного кода Mono не менялась ВОСЕМЬ ЛЕТ, за исключением незначительного улучшения производительности в 2007 году. - person Warren Rumak; 27.01.2014
comment
@Warren: Не стесняйтесь вывести результирующую строку, если вы действительно обеспокоены, но давайте будем совершенно честны: разница между 20 и 40 итерациями по сравнению со строкой длиной вашего среднего пути (скажем, 100 символов, чтобы быть щедрым) сделает ровно никакой разницы во времени выполнения вашей функции. Для всех практических целей об этом не нужно беспокоиться. С другой стороны, эти две функции служат разным целям и (по крайней мере, на мой взгляд) было бы вполне разумно, чтобы одна функция не возвращала надмножество другой для некоторой данной файловой системы. - person Matthew Scharley; 29.01.2014
comment
Как может удвоение работы (будь то дедупликация массива или двукратное прохождение через почти одни и те же значения массива) абсолютно без разницы? Вы не хуже меня знаете, что это неправильно, поэтому -не--говори- -это-. Мы пытаемся быть образовательным ресурсом в Stackoverflow, а не местом для риторических расцветов, вызванных тем, что вы ошибаетесь. Давайте проясним: то, что вы здесь рекомендуете, фактически то же самое, что и старая утка Daily WTF о предоставлении вашего собственного определения ИСТИНА и ЛОЖЬ, потому что вы не можете доверять компилятору или библиотекам, чтобы всегда делать это правильно. - person Warren Rumak; 29.01.2014
comment
GetInvalidFileNameChars () всегда - ВСЕГДА, вы меня слышите - будет включать все в GetInvalidPathChars (), потому что файл не может содержать недопустимый символ в имени пути. Ни одна файловая система сегодня не позволяет этого, ни одна файловая система никогда не позволит. В любом случае, собственная документация Microsoft по этим функциям очень четко заявляет, что не следует ожидать, что список символов будет гарантированно точным, потому что файловые системы в любом случае могут поддерживать что-то другое. - person Warren Rumak; 29.01.2014
comment
Я бы, наверное, встал на сторону Мэтью и просто сказал, что это предположение - мать всех неудач. Вы говорите об оптимизации кода, который, вероятно, не требует оптимизации из-за потенциальной правильности. Я бы принял правильность над преждевременной оптимизацией в любой день - person Charleh; 15.03.2014
comment
@Charleh это обсуждение настолько ненужно ... код всегда должен быть оптимизирован, и нет риска, что это будет неправильным. Имя файла также является частью пути. Так что нелогично, что GetInvalidPathChars() может содержать символы, которых GetInvalidFileNameChars() не будет. Вы не ставите правильность выше преждевременной оптимизации. Вы просто используете плохой код. - person Stefan Fabian; 09.08.2014
comment
Лично я бы предпочел такой способ: var invalid = Path.GetInvalidFileNameChars().Union(Path.GetInvalidPathChars()); foreach(char c in invalid) illegal = illegal.Replace(c.ToString(), "_"); - person Tim Schmelter; 09.09.2015
comment
Я не уверен, почему вы, ребята, так любопытны, почему он хочет это использовать. Существуют различные допустимые сценарии, в которых это может быть полезно. Наше приложение, например, выводит файлы xlsx по электронной почте в виде отчетов, и если мы не проверим его при входе, вы не узнаете до запланированного времени создания отчета, что имя файла недействительно. У нас были проблемы, когда в прошлом кто-то случайно вводил «меньше чем» в имени файла и сохранял его. Кроме того, некоторые из наших клиентов запускают Linux, а некоторые - окна, поэтому разрешенные файлы не совпадают. - person John Lord; 30.11.2018
comment
@JohnLord - еще один распространенный вариант использования, связанный с именами файлов, поступающими из внешних электронных писем. Вы не можете контролировать отправляемое вам имя файла. Вы, конечно, можете выбросить оригинал и заменить его чем-то собственным изобретением, но бывают случаи, когда вы хотите сохранить как можно больше оригинала для целей ИИ. - person Byron; 03.06.2020

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

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

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

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

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

person Shehab Fawzy    schedule 20.04.2014
comment
Чтобы точно ответить на вопрос OP, вам нужно будет использовать вместо _, но ваш ответ, вероятно, применим к большему количеству из нас на практике. Я думаю, чаще всего делается замена недопустимых символов на какие-то допустимые. - person B H; 08.01.2016
comment
Я протестировал пять методов из этого вопроса (временной цикл 100000), и этот метод является самым быстрым. Регулярное выражение заняло 2-е место и было на 25% медленнее, чем этот метод. - person Brain2000; 15.07.2016
comment
Чтобы обратиться к комментарию @BH, можно просто использовать string.Concat (name.Split (Path.GetInvalidFileNameChars ())) - person Michael Sutton; 07.06.2017

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

person Michael Minton    schedule 12.09.2011
comment
У меня это не сработало. Метод не возвращает чистую строку. Он возвращает переданное имя файла как есть. - person Karan; 17.07.2013
comment
Что сказал @Karan, это не работает, возвращается исходная строка. - person Jon; 20.03.2014
comment
На самом деле вы можете сделать это с помощью Linq вот так: var invalid = new HashSet<char>(Path.GetInvalidPathChars()); return new string(originalString.Where(s => !invalid.Contains(s)).ToArray()). Производительность, вероятно, невелика, но это, вероятно, не имеет значения. - person Casey; 09.07.2015
comment
@Karan или Jon Какой вклад вы отправляете этой функции? См. Мою правку для проверки этого метода. - person Michael Minton; 24.09.2015
comment
Это просто - парни передавали строки с действительными символами. Проголосовали за крутое решение для агрегатов. - person Nickmaovich; 20.01.2016
comment
Очень хорошее решение, но очищает только имя файла (как указано), но не фактический путь, поскольку он рассматривает \ как недопустимый символ, и если у вас есть что-то вроде \\ MyServer \ e $ \ demo \ Output \ Test \ 1111_joe_soap.pdf, это возвращает MyServere $ demoOutputTest1111_joe_soap.pdf - person Thierry; 16.03.2017
comment
Плюс 1 за творческое использование агрегата. - person Teejay; 17.07.2020

Вы можете удалить недопустимые символы с помощью 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());
person Gregor Slavec    schedule 24.11.2010
comment
Мне нравится этот способ: вы сохраняете только разрешенные символы в строке (которая представляет собой не что иное, как массив символов). - person Dude Pascalou; 04.07.2012
comment
Я знаю, что это старый вопрос, но это отличный ответ. Однако я хотел добавить, что в C # вы не можете преобразовать char [] в строку ни неявно, ни явно (сумасшедший, я знаю), поэтому вам нужно перетащить его в конструктор строк. - person JNYRanger; 21.10.2014
comment
Я не подтвердил это, но ожидаю, что Path.GetInvalidPathChars () будет надмножеством GetInvalidFileNameChars () и будет охватывать как имена файлов, так и пути, поэтому я бы, вероятно, использовал это вместо этого. - person angularsen; 10.01.2015
comment
@anjdreas на самом деле Path.GetInvalidPathChars () кажется подмножеством Path.GetInvalidFileNameChars (), а не наоборот. Например, Path.GetInvalidPathChars () не вернет "?". - person Rafael Costa; 30.12.2015
comment
Это хороший ответ. Я использую как список имен файлов, так и список путей к файлам: ____________________________ string cleanData = new string (data.Where (x = ›! Path.GetInvalidFileNameChars (). Contains (x) &&! Path.GetInvalidPathChars (). Contains (x)). ToArray ()); - person goamn; 30.11.2017
comment
Вы также можете сделать var invalidChars = new HashSet ‹char› (Path.GetInvalidFileNameChars ()) и сделать его O (n) вместо O (n ^ 2). Почему бы и нет. - person Cesar; 24.09.2020

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

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

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

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

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

person Lily Finley    schedule 11.02.2014

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

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

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

person René    schedule 16.11.2011
comment
Тогда в чем смысл Path.GetInvalidFileNameChars? Я ожидал, что он вернет именно недопустимые символы для текущей системы, полагаясь на .NET, чтобы знать, в какой файловой системе я работаю, и представляя мне подходящие недопустимые символы. Если это не так, и он просто возвращает жестко закодированные символы, которые в первую очередь ненадежны, этот метод следует удалить, поскольку он имеет нулевое значение. - person Jan; 18.01.2014
comment
Я знаю, что это старый комментарий, но @Jan вы могли бы захотеть написать в другой файловой системе, возможно, поэтому есть предупреждение. - person fantastik78; 07.07.2015
comment
@ fantastik78 - хороший аргумент, но в этом случае я хотел бы иметь дополнительный аргумент перечисления, чтобы указать мою удаленную FS. Если это слишком много усилий по техническому обслуживанию (что наиболее вероятно), весь этот метод по-прежнему является плохой идеей, потому что он создает неправильное впечатление о безопасности. - person Jan; 03.09.2015
comment
@Jan Я полностью с тобой согласен, я просто спорил из-за предупреждения. - person fantastik78; 03.09.2015
comment
Интересно, что это своего рода занесение в черный список недопустимых символов. Не лучше ли занести в белый список только известные допустимые символы ?! Напоминает мне глупую идею сканера вирусов вместо добавления разрешенных приложений в белый список .... - person Bernhard; 10.07.2018
comment
Обратите внимание на то, что в предупреждении указаны имена файлов. На самом деле он говорит вам, что он не проверяет сами имена файлов, а только недопустимые символы. У вас все еще может быть неправильное имя файла, которое является зарезервированным словом. Также как бы вы занесли приложение в белый список? Я бы просто сделал так, чтобы у моего вируса было ваше имя файла и подпись. - person John Lord; 29.11.2018

Лучший способ удалить недопустимый символ из пользовательского ввода - заменить недопустимый символ с помощью класса 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">
person anomepani    schedule 28.09.2013
comment
IMHO это решение намного лучше, чем другие. Вместо поиска всех недопустимых символов просто определите, какие из них действительны. - person igorushi; 29.09.2015
comment
Для полностью переносимых имен файлов POSIX используйте "[^a-zA-Z0-9_.-]+" - person CrazyTim; 23.07.2021

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

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

person user7116    schedule 28.09.2008
comment
Особенно согласен со вторым советом. - person OregonGhost; 28.09.2008
comment
Обычно я согласен со вторым, но у меня есть программа, которая генерирует имя файла и в некоторых ситуациях может содержать недопустимые символы. Поскольку моя программа генерирует недопустимые имена файлов, я считаю целесообразным удалить / заменить эти символы. (Просто укажите допустимый вариант использования) - person JDB still remembers Monica; 09.05.2013

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

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

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

person Jeff Yates    schedule 28.09.2008
comment
Странно, у меня это сработало. Я перепроверяю это, когда у меня будет возможность. Не могли бы вы быть более конкретными и объяснить, что именно вам не подходит? - person Jeff Yates; 08.02.2010
comment
Это не сработает (по крайней мере, правильно), потому что вы неправильно экранируете символы пути, а некоторые из них имеют особое значение. Обратитесь к моему ответу, чтобы узнать, как это сделать. - person Matthew Scharley; 09.04.2010
comment
@Jeff: Ваша версия все равно лучше, чем у Мэтью, если вы немного ее измените. Обратитесь к моему ответу о том, как. - person Jan; 13.02.2012
comment
Я бы также добавил несколько других шаблонов недопустимых имен файлов, которые можно найти на 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); - person yar_shukan; 10.09.2014
comment
Небольшое улучшение синтаксиса для комментария @yar_shukan: добавьте @ перед строковым выражением, если вы столкнулись с ошибкой Нераспознанная escape-последовательность, т.е. String.Format(@"^CON| ... )" - person hotenov; 26.09.2020

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

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

Улучшение заключается в том, чтобы избежать автоматически сгенерированного регулярного выражения.

person Jan    schedule 15.02.2011

Вот фрагмент кода, который должен помочь для .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, "");
    }
}
person James    schedule 19.10.2010

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

wvd_vegt

person wvd_vegt    schedule 19.06.2012
comment
+1: Совершенно верно. Сегодня, работая в .NET 4.0, решение с регулярным выражением из верхнего ответа уничтожило все обратные косые черты на полном пути. Поэтому я сделал регулярное выражение для пути к каталогу и регулярное выражение только для имени файла, очищенное отдельно и рекомбинированное - person dario_ramos; 23.05.2013
comment
Это может быть правдой, но это не отвечает на вопрос. Я не уверен, что расплывчатое «Я бы сделал это так» ужасно полезно по сравнению с некоторыми из уже имеющихся здесь полных решений (см., Например, ответ Лилли ниже) - person Ian Grainger; 12.05.2016

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

<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
person Maxence    schedule 01.10.2014

Выбросить исключение.

if ( fileName.IndexOfAny(Path.GetInvalidFileNameChars()) > -1 )
            {
                throw new ArgumentException();
            }
person mirezus    schedule 12.03.2009
comment
Я не думаю, что здесь ценно создание исключения, поскольку в вопросе говорится об удалении недопустимых символов, а не просто о создании исключения. - person PHenry; 20.11.2020

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

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;
    }
}
person Johan Larsson    schedule 07.12.2013
comment
Мне это нравится, потому что это позволяет избежать двух разных строк, создающих один и тот же результирующий путь. - person Kim; 29.01.2014

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

person Backs    schedule 25.07.2018

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

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

person Sandor Davidhazi    schedule 28.09.2008
comment
Учитывая, что .net - это фреймворк, который предназначен для запуска программ на нескольких платформах (например, Linux / Unix, а также Windows), я считаю, что Path.GetInvalidFileNameChars () лучше всего, поскольку он будет содержать информацию о том, что есть, а что нет. t действителен для файловой системы, в которой выполняется ваша программа. Даже если ваша программа никогда не будет работать в Linux (возможно, она полна кода WPF), всегда есть шанс, что в будущем появится новая файловая система Windows с другими допустимыми / недопустимыми символами. Прокатывая собственное с помощью регулярного выражения, вы изобретаете колесо и переносите проблему платформы в ваш собственный код. - person Daniel Scott; 04.10.2018
comment
Я согласен с вашим советом относительно онлайн-редакторов / тестеров регулярных выражений. Я считаю их бесценными (поскольку регулярные выражения - штука сложная и полна тонкости, которая может легко сбить вас с толку, давая вам регулярное выражение, которое ведет себя совершенно неожиданным образом с крайними случаями). Мне больше всего нравится regex101.com (мне нравится, как он разбивает регулярное выражение и ясно показывает, что он ожидает найти). Мне также очень нравится debuggex.com, поскольку он имеет компактное визуальное представление групп совпадений, классов символов и прочего. - person Daniel Scott; 04.10.2018

Это похоже на 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());
    }
person Alexey F    schedule 09.02.2015
comment
Я не думаю, что это O (n), когда вы используете функцию «Any». - person II ARROWS; 30.08.2016
comment
@IIARROWS а что это на ваш взгляд? - person Alexey F; 30.08.2016
comment
Я не знаю, я просто не чувствовал этого, когда писал свой комментарий ... теперь, когда я попытался его вычислить, похоже, вы правы. - person II ARROWS; 30.08.2016
comment
Я выбрал этот из-за вашего соображения производительности. Спасибо. - person Berend Engelbrecht; 16.10.2019

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

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

В прошлом я был очень удивлен (шокирован) тем, насколько быстро хэш-набор (или словарь) превосходит итерацию по списку. Для строк это смехотворно мало (около 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;
    }
}
person Daniel Scott    schedule 08.09.2017

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

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

person aemre    schedule 22.02.2018

Один лайнер для очистки строки от любых недопустимых символов для именования файлов 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, "");
person Zananok    schedule 02.12.2018

Вот мой небольшой вклад. Метод для замены в той же строке без создания новых строк или построителей строк. Это быстро, легко для понимания и является хорошей альтернативой всем упомянутым в этом посте.

private static HashSet<char> _invalidCharsHash;
private static HashSet<char> InvalidCharsHash
{
  get { return _invalidCharsHash ?? (_invalidCharsHash = new HashSet<char>(Path.GetInvalidFileNameChars())); }
}

private static string ReplaceInvalidChars(string fileName, string newValue)
{
  char newChar = newValue[0];

  char[] chars = fileName.ToCharArray();
  for (int i = 0; i < chars.Length; i++)
  {
    char c = chars[i];
    if (InvalidCharsHash.Contains(c))
      chars[i] = newChar;
  }

  return new string(chars);
}

Вы можете назвать это так:

string illegal = "\"M<>\"\\a/ry/ h**ad:>> a\\/:*?\"<>| li*tt|le|| la\"mb.?";
string legal = ReplaceInvalidChars(illegal);

и возвращает:

_M ____a_ry_ h__ad___ a_________ li_tt_le__ la_mb._

Стоит отметить, что этот метод всегда заменяет недопустимые символы заданным значением, но не удаляет их. Если вы хотите удалить недопустимые символы, эта альтернатива поможет:

private static string RemoveInvalidChars(string fileName, string newValue)
{
  char newChar = string.IsNullOrEmpty(newValue) ? char.MinValue : newValue[0];
  bool remove = newChar == char.MinValue;

  char[] chars = fileName.ToCharArray();
  char[] newChars = new char[chars.Length];
  int i2 = 0;
  for (int i = 0; i < chars.Length; i++)
  {
    char c = chars[i];
    if (InvalidCharsHash.Contains(c))
    {
      if (!remove)
        newChars[i2++] = newChar;
    }
    else
      newChars[i2++] = c;

  }

  return new string(newChars, 0, i2);
}

ЭТАЛОН

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

Примечание. В этом посте предлагаются методы Test1 и Test2.

Первый запуск

replacing with '_', 1000000 iterations

Результаты:

============Test1===============
Elapsed=00:00:01.6665595
Result=_M ____a_ry_ h__ad___ a_________ li_tt_le__ la_mb._

============Test2===============
Elapsed=00:00:01.7526835
Result=_M ____a_ry_ h__ad___ a_________ li_tt_le__ la_mb._

============Test3===============
Elapsed=00:00:05.2306227
Result=_M ____a_ry_ h__ad___ a_________ li_tt_le__ la_mb._

============Test4===============
Elapsed=00:00:14.8203696
Result=_M ____a_ry_ h__ad___ a_________ li_tt_le__ la_mb._

============Test5===============
Elapsed=00:00:01.8273760
Result=_M ____a_ry_ h__ad___ a_________ li_tt_le__ la_mb._

============Test6===============
Elapsed=00:00:05.4249985
Result=_M ____a_ry_ h__ad___ a_________ li_tt_le__ la_mb._

============Test7===============
Elapsed=00:00:07.5653833
Result=_M ____a_ry_ h__ad___ a_________ li_tt_le__ la_mb._

============Test8===============
Elapsed=00:12:23.1410106
Result=_M ____a_ry_ h__ad___ a_________ li_tt_le__ la_mb._

============Test9===============
Elapsed=00:00:02.1016708
Result=_M ____a_ry_ h__ad___ a_________ li_tt_le__ la_mb._

============Test10===============
Elapsed=00:00:05.0987225
Result=M ary had a little lamb.

============Test11===============
Elapsed=00:00:06.8004289
Result=M ary had a little lamb.

Второй запуск

removing invalid chars, 1000000 iterations

Примечание: Test1 не удалит, а только заменит.

Результаты:

============Test1===============
Elapsed=00:00:01.6945352
Result= M     a ry  h  ad    a          li tt le   la mb.

============Test2===============
Elapsed=00:00:01.4798049
Result=M ary had a little lamb.

============Test3===============
Elapsed=00:00:04.0415688
Result=M ary had a little lamb.

============Test4===============
Elapsed=00:00:14.3397960
Result=M ary had a little lamb.

============Test5===============
Elapsed=00:00:01.6782505
Result=M ary had a little lamb.

============Test6===============
Elapsed=00:00:04.9251707
Result=M ary had a little lamb.

============Test7===============
Elapsed=00:00:07.9562379
Result=M ary had a little lamb.

============Test8===============
Elapsed=00:12:16.2918943
Result=M ary had a little lamb.

============Test9===============
Elapsed=00:00:02.0770277
Result=M ary had a little lamb.

============Test10===============
Elapsed=00:00:05.2721232
Result=M ary had a little lamb.

============Test11===============
Elapsed=00:00:05.2802903
Result=M ary had a little lamb.

ЭТАЛОННЫЕ РЕЗУЛЬТАТЫ

Методы Test1, Test2 и Test5 - самые быстрые. Метод Test8 самый медленный.

КОД

Вот полный код теста:

private static HashSet<char> _invalidCharsHash;
private static HashSet<char> InvalidCharsHash
{
  get { return _invalidCharsHash ?? (_invalidCharsHash = new HashSet<char>(Path.GetInvalidFileNameChars())); }
}

private static string _invalidCharsValue;
private static string InvalidCharsValue
{
  get { return _invalidCharsValue ?? (_invalidCharsValue = new string(Path.GetInvalidFileNameChars())); }
}

private static char[] _invalidChars;
private static char[] InvalidChars
{
  get { return _invalidChars ?? (_invalidChars = Path.GetInvalidFileNameChars()); }
}

static void Main(string[] args)
{
  string testPath = "\"M <>\"\\a/ry/ h**ad:>> a\\/:*?\"<>| li*tt|le|| la\"mb.?";

  int max = 1000000;
  string newValue = "";

  TimeBenchmark(max, Test1, testPath, newValue);
  TimeBenchmark(max, Test2, testPath, newValue);
  TimeBenchmark(max, Test3, testPath, newValue);
  TimeBenchmark(max, Test4, testPath, newValue);
  TimeBenchmark(max, Test5, testPath, newValue);
  TimeBenchmark(max, Test6, testPath, newValue);
  TimeBenchmark(max, Test7, testPath, newValue);
  TimeBenchmark(max, Test8, testPath, newValue);
  TimeBenchmark(max, Test9, testPath, newValue);
  TimeBenchmark(max, Test10, testPath, newValue);
  TimeBenchmark(max, Test11, testPath, newValue);

  Console.Read();
}

private static void TimeBenchmark(int maxLoop, Func<string, string, string> func, string testString, string newValue)
{
  var sw = new Stopwatch();
  sw.Start();
  string result = string.Empty;

  for (int i = 0; i < maxLoop; i++)
    result = func?.Invoke(testString, newValue);

  sw.Stop();

  Console.WriteLine($"============{func.Method.Name}===============");
  Console.WriteLine("Elapsed={0}", sw.Elapsed);
  Console.WriteLine("Result={0}", result);
  Console.WriteLine("");
}

private static string Test1(string fileName, string newValue)
{
  char newChar = string.IsNullOrEmpty(newValue) ? char.MinValue : newValue[0];

  char[] chars = fileName.ToCharArray();
  for (int i = 0; i < chars.Length; i++)
  {
    if (InvalidCharsHash.Contains(chars[i]))
      chars[i] = newChar;
  }

  return new string(chars);
}

private static string Test2(string fileName, string newValue)
{
  char newChar = string.IsNullOrEmpty(newValue) ? char.MinValue : newValue[0];
  bool remove = newChar == char.MinValue;

  char[] chars = fileName.ToCharArray();
  char[] newChars = new char[chars.Length];
  int i2 = 0;
  for (int i = 0; i < chars.Length; i++)
  {
    char c = chars[i];
    if (InvalidCharsHash.Contains(c))
    {
      if (!remove)
        newChars[i2++] = newChar;
    }
    else
      newChars[i2++] = c;

  }

  return new string(newChars, 0, i2);
}

private static string Test3(string filename, string newValue)
{
  foreach (char c in InvalidCharsValue)
  {
    filename = filename.Replace(c.ToString(), newValue);
  }

  return filename;
}

private static string Test4(string filename, string newValue)
{
  Regex r = new Regex(string.Format("[{0}]", Regex.Escape(InvalidCharsValue)));
  filename = r.Replace(filename, newValue);
  return filename;
}

private static string Test5(string filename, string newValue)
{
  return string.Join(newValue, filename.Split(InvalidChars));
}

private static string Test6(string fileName, string newValue)
{
  return InvalidChars.Aggregate(fileName, (current, c) => current.Replace(c.ToString(), newValue));
}

private static string Test7(string fileName, string newValue)
{
  string regex = string.Format("[{0}]", Regex.Escape(InvalidCharsValue));
  return Regex.Replace(fileName, regex, newValue, RegexOptions.Compiled);
}

private static string Test8(string fileName, string newValue)
{
  string regex = string.Format("[{0}]", Regex.Escape(InvalidCharsValue));
  Regex removeInvalidChars = new Regex(regex, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant);
  return removeInvalidChars.Replace(fileName, newValue);
}

private static string Test9(string fileName, string newValue)
{
  StringBuilder sb = new StringBuilder(fileName.Length);
  bool changed = false;

  for (int i = 0; i < fileName.Length; i++)
  {
    char c = fileName[i];
    if (InvalidCharsHash.Contains(c))
    {
      changed = true;
      sb.Append(newValue);
    }
    else
      sb.Append(c);
  }

  if (sb.Length == 0)
    return newValue;

  return changed ? sb.ToString() : fileName;
}

private static string Test10(string fileName, string newValue)
{
  if (!fileName.Any(c => InvalidChars.Contains(c)))
  {
    return fileName;
  }

  return new string(fileName.Where(c => !InvalidChars.Contains(c)).ToArray());
}

private static string Test11(string fileName, string newValue)
{
  string invalidCharsRemoved = new string(fileName
    .Where(x => !InvalidChars.Contains(x))
    .ToArray());

  return invalidCharsRemoved;
}
person c-chavez    schedule 29.09.2020

Это позволит вам избежать столкновений.

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

    }
person mcintyre321    schedule 19.09.2014

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

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;
}
person Suplanus    schedule 07.07.2015

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

  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();
    }
}
person schoetbi    schedule 14.06.2018

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

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, '_');
person Hans-Peter Kalb    schedule 14.05.2020
comment
В дополнение к предоставленному вами ответу, пожалуйста, рассмотрите возможность предоставления краткого объяснения того, почему и как это решает проблему. - person jtate; 14.05.2020

Я использовал свой собственный метод, который, кажется, намного быстрее других опубликованных здесь (особенно регулярное выражение, которое так медленно), но я не тестировал все опубликованные методы.

https://dotnetfiddle.net/haIXiY

Первый метод (мой) и второй (тоже мой, но старый) также дополнительно проверяют обратную косую черту, поэтому тест не идеален, но в любом случае он просто дает вам представление.

Результат на моем ноутбуке (за 100 000 итераций):

StringHelper.RemoveInvalidCharacters 1: 451 ms  
StringHelper.RemoveInvalidCharacters 2: 7139 ms  
StringHelper.RemoveInvalidCharacters 3: 2447 ms  
StringHelper.RemoveInvalidCharacters 4: 3733 ms  
StringHelper.RemoveInvalidCharacters 5: 11689 ms  (==> Regex!)

Самый быстрый способ:

public static string RemoveInvalidCharacters(string content, char replace = '_', bool doNotReplaceBackslashes = false)
{
    if (string.IsNullOrEmpty(content))
        return content;

    var idx = content.IndexOfAny(InvalidCharacters);
    if (idx >= 0)
    {
        var sb = new StringBuilder(content);
        while (idx >= 0)
        {
            if (sb[idx] != '\\' || !doNotReplaceBackslashes)
                sb[idx] = replace;
            idx = content.IndexOfAny(InvalidCharacters, idx+1);
        }
        return sb.ToString();
    }
    return content;
}

Метод не компилируется как dur для свойства InvalidCharacters, проверьте скрипт на наличие полного кода

person Fabske    schedule 23.10.2020

Если вам нужно использовать этот метод во многих местах проекта, вы также можете создать метод расширения и вызывать его в любом месте проекта для строк.

 public static class StringExtension
    {
        public static string RemoveInvalidChars(this string originalString)
        {            
            string finalString=string.Empty;
            if (!string.IsNullOrEmpty(originalString))
            {
                return string.Concat(originalString.Split(Path.GetInvalidFileNameChars()));
            }
            return finalString;            
        }
    }

Вы можете вызвать вышеуказанный метод расширения как:

string illegal = "\"M<>\"\\a/ry/ h**ad:>> a\\/:*?\"<>| li*tt|le|| la\"mb.?";
string afterIllegalChars = illegal.RemoveInvalidChars();
person Simant    schedule 04.02.2021
comment
Потому что каждая строка - это путь. Или почему имеет смысл расширять string только на один особый случай? - person Andreas; 05.02.2021

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

[YOUR STRING].Replace('\\', ' ').Replace('/', ' ').Replace('"', ' ').Replace('*', ' ').Replace(':', ' ').Replace('?', ' ').Replace('<', ' ').Replace('>', ' ').Replace('|', ' ').Trim();
person Danny Fallas    schedule 15.01.2014