12 ответов:
попробуйте что-то вроде этого:
string fileName = "something"; foreach (char c in System.IO.Path.GetInvalidFileNameChars()) { fileName = fileName.Replace(c, '_'); }Edit:
С
GetInvalidFileNameChars()вернет 10 или 15 символов, лучше использоватьStringBuilderвместо простой строки; исходная версия займет больше времени и потребляет больше памяти.
fileName = fileName.Replace(":", "-")однако ": "не является единственным незаконным символом для Windows. Вам также придется обрабатывать:
/, \, :, *, ?, ", <, > and |они содержатся в System. IO. Path. GetInvalidFileNameChars ();
также (на Windows),"."не может быть единственным символом в имени файла (оба ".","..","...", и так далее, являются недействительными). Будьте осторожны при именовании файлов с помощью ".", например:
echo "test" > .test.создает файл с именем ".тест"
наконец, если вы действительно хотите делать все правильно, есть некоторые специальные наименования файла вы должны высматривать. На Windows вы не можете создавать файлы с именем:
CON, PRN, AUX, CLOCK$, NUL COM0, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9 LPT0, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, and LPT9.
это не более эффективно, но это более весело :)
var fileName = "foo:bar"; var invalidChars = System.IO.Path.GetInvalidFileNameChars(); var cleanFileName = new string(fileName.Where(m => !invalidChars.Contains(m)).ToArray<char>());
в случае, если кто-то хочет оптимизированную версию на основе
StringBuilderиспользуйте этот. Включает в себя трюк rkagerer в качестве опции.static char[] _invalids; /// <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 simply remove bad characters.</param> /// <param name="fancy">Whether 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, returns "_".</returns> public static string MakeValidFileName(string text, char? replacement = '_', bool fancy = true) { StringBuilder sb = new StringBuilder(text.Length); var invalids = _invalids ?? (_invalids = Path.GetInvalidFileNameChars()); bool changed = false; for (int i = 0; i < text.Length; i++) { char c = text[i]; if (invalids.Contains(c)) { changed = true; var repl = replacement ?? ''; if (fancy) { if (c == '"') repl = '”'; // U+201D right double quotation mark else if (c == '\'') repl = '’'; // U+2019 right single quotation mark else if (c == '/') repl = '⁄'; // U+2044 fraction slash } if (repl != '') sb.Append(repl); } else sb.Append(c); } if (sb.Length == 0) return "_"; return changed ? sb.ToString() : text; }
у Диего есть правильное решение, но там есть одна очень маленькая ошибка. Версия строки.Заменить используется должна быть строка.Заменить(char, char), нет строки.Заменить(char, string)
Я не могу редактировать ответ или я бы просто сделал небольшое изменение.
так и должно быть:
string fileName = "something"; foreach (char c in System.IO.Path.GetInvalidFileNameChars()) { fileName = fileName.Replace(c, '_'); }
вот небольшой поворот в ответе Диего.
Если вы не боитесь Unicode, вы можете сохранить немного больше точности, заменив недопустимые символы допустимыми символами Unicode, которые похожи на них. Вот код, который я использовал в недавнем проекте с использованием списков пиломатериалов:
static string MakeValidFilename(string text) { text = text.Replace('\'', '’'); // U+2019 right single quotation mark text = text.Replace('"', '”'); // U+201D right double quotation mark text = text.Replace('/', '⁄'); // U+2044 fraction slash foreach (char c in System.IO.Path.GetInvalidFileNameChars()) { text = text.Replace(c, '_'); } return text; }это производит имена файлов, как
1⁄2” spruce.txtвместо1_2_ spruce.txtДа, это действительно работает:
нюанс Риски
Я знал, что этот трюк будет работать на NTFS, но был удивлен, обнаружив, что он также работает на разделах FAT и FAT32. Это потому что длинные имена и хранится в Юникоде, даже до как Windows 95 / NT. Я тестировал на Win7, XP и даже на маршрутизаторе на базе Linux, и они появились нормально. Не могу сказать то же самое для внутри DOSBox.
тем не менее, прежде чем сходить с ума с этим, подумайте, действительно ли вам нужно дополнительная верность. Внешний вид Unicode может запутать людей или старые программы, например, более старые ОС полагаются на кодовые страницы.
немного очистив мой код и сделав небольшой рефакторинг... Я создал расширение для строкового типа:
public static string ToValidFileName(this string s, char replaceChar = '_', char[] includeChars = null) { var invalid = Path.GetInvalidFileNameChars(); if (includeChars != null) invalid = invalid.Union(includeChars).ToArray(); return string.Join(string.Empty, s.ToCharArray().Select(o => o.In(invalid) ? replaceChar : o)); }теперь его проще использовать с:
var name = "Any string you want using ? / \ or even +.zip"; var validFileName = name.ToValidFileName();если вы хотите заменить другим символом, чем"_", вы можете использовать:
var validFileName = name.ToValidFileName(replaceChar:'#');и вы можете добавить символы для замены.. например, вы не хотите пробелы или запятые:
var validFileName = name.ToValidFileName(includeChars: new [] { ' ', ',' });надеюсь, что это помогает...
Ура
вот версия, которая использует
StringBuilderиIndexOfAnyс Навальным добавьте для полной эффективности. Он также возвращает исходную строку, а не создает дубликат строки.и последнее, но не менее важное: у него есть оператор switch, который возвращает похожие символы, которые вы можете настроить любым способом. Проверьте Unicode.org ' s confusables lookup чтобы увидеть, какие параметры вы могли бы иметь, в зависимости от шрифта.
public static string GetSafeFilename(string arbitraryString) { var invalidChars = System.IO.Path.GetInvalidFileNameChars(); var replaceIndex = arbitraryString.IndexOfAny(invalidChars, 0); if (replaceIndex == -1) return arbitraryString; var r = new StringBuilder(); var i = 0; do { r.Append(arbitraryString, i, replaceIndex - i); switch (arbitraryString[replaceIndex]) { case '"': r.Append("''"); break; case '<': r.Append('\u02c2'); // '˂' (modifier letter left arrowhead) break; case '>': r.Append('\u02c3'); // '˃' (modifier letter right arrowhead) break; case '|': r.Append('\u2223'); // '∣' (divides) break; case ':': r.Append('-'); break; case '*': r.Append('\u2217'); // '∗' (asterisk operator) break; case '\': case '/': r.Append('\u2044'); // '⁄' (fraction slash) break; case '': case '\f': case '?': break; case '\t': case '\n': case '\r': case '\v': r.Append(' '); break; default: r.Append('_'); break; } i = replaceIndex + 1; replaceIndex = arbitraryString.IndexOfAny(invalidChars, i); } while (replaceIndex != -1); r.Append(arbitraryString, i, arbitraryString.Length - i); return r.ToString(); }это не
.,.., или зарезервированные имена, такие какCONпотому что не ясно, что замена должна быть.
вот версия принятого ответа с помощью
LinqиспользуетEnumerable.Aggregate:string fileName = "something"; Path.GetInvalidFileNameChars() .Aggregate(fileName, (current, c) => current.Replace(c, '_'));
еще одно простое решение:
private string MakeValidFileName(string original, char replacementChar = '_') { var invalidChars = new HashSet<char>(Path.GetInvalidFileNameChars()); return new string(original.Select(c => invalidChars.Contains(c) ? replacementChar : c).ToArray()); }
Мне нужно было сделать это сегодня... в моем случае мне нужно было связать имя клиента с датой и временем для финала .файл KMZ. Мое окончательное решение было таково:
string name = "Whatever name with valid/invalid chars"; char[] invalid = System.IO.Path.GetInvalidFileNameChars(); string validFileName = string.Join(string.Empty, string.Format("{0}.{1:G}.kmz", name, DateTime.Now) .ToCharArray().Select(o => o.In(invalid) ? '_' : o));вы даже можете заменить пробелы, если вы добавите символ пробела в недопустимый массив.
может быть, это не самый быстрый, но поскольку производительность не была проблемой, я нашел ее элегантной и понятной.
Ура!
Вы можете сделать это с помощью :
sed -e " s/[?()\[\]=+<>:;©®”,*|]/_/g s/"$'\t'"/ /g s/–/-/g s/\"/_/g s/[[:cntrl:]]/_/g"

Comments