Моя программа будет брать произвольные строки из Интернета и использовать их для имен файлов. Есть ли простой способ удалить плохие символы из этих строк или мне нужно написать для этого специальную функцию?
Моя программа будет брать произвольные строки из Интернета и использовать их для имен файлов. Есть ли простой способ удалить плохие символы из этих строк или мне нужно написать для этого специальную функцию?
Ответы:
Ух, ненавижу, когда люди пытаются угадать, какие символы действительны. Помимо того, что они полностью непереносимы (всегда думают о Mono), в обоих предыдущих комментариях пропущено более 25 недопустимых символов.
'Clean just a filename
Dim filename As String = "salmnas dlajhdla kjha;dmas'lkasn"
For Each c In IO.Path.GetInvalidFileNameChars
filename = filename.Replace(c, "")
Next
'See also IO.Path.GetInvalidPathChars
Чтобы удалить недопустимые символы:
static readonly char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
// Builds a string out of valid chars
var validFilename = new string(filename.Where(ch => !invalidFileNameChars.Contains(ch)).ToArray());
Чтобы заменить недопустимые символы:
static readonly char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
// Builds a string out of valid chars and an _ for invalid ones
var validFilename = new string(filename.Select(ch => invalidFileNameChars.Contains(ch) ? '_' : ch).ToArray());
Чтобы заменить недопустимые символы (и избежать потенциального конфликта имен, например Hell * vs Hell $):
static readonly IList<char> invalidFileNameChars = Path.GetInvalidFileNameChars();
// Builds a string out of valid chars and replaces invalid chars with a unique letter (Moves the Char into the letter range of unicode, starting at "A")
var validFilename = new string(filename.Select(ch => invalidFileNameChars.Contains(ch) ? Convert.ToChar(invalidFileNameChars.IndexOf(ch) + 65) : ch).ToArray());
Этот вопрос задавался много раз раньше, и, как уже неоднократно отмечалось, IO.Path.GetInvalidFileNameChars
он неадекватен.
Во-первых, есть много имен, таких как PRN и CON, которые зарезервированы и не допускаются для имен файлов. Есть другие имена, которые нельзя использовать только в корневой папке. Имена, оканчивающиеся на точку, также не допускаются.
Во-вторых, существует множество ограничений по длине. Полный список NTFS читайте здесь .
В-третьих, вы можете подключаться к файловым системам, которые имеют другие ограничения. Например, имена файлов ISO 9660 не могут начинаться с символа «-», но могут содержать его.
В-четвертых, что делать, если два процесса «произвольно» выбирают одно и то же имя?
В общем, использование имен файлов, сгенерированных извне, - плохая идея. Я предлагаю сгенерировать ваши собственные личные имена файлов и хранить внутри себя удобочитаемые имена.
Я согласен с Грауэнвольфом и очень рекомендую Path.GetInvalidFileNameChars()
Вот мой вклад в C #:
string file = @"38?/.\}[+=n a882 a.a*/|n^%$ ad#(-))";
Array.ForEach(Path.GetInvalidFileNameChars(),
c => file = file.Replace(c.ToString(), String.Empty));
ps - это более загадочно, чем должно быть - я пытался быть кратким.
Array.ForEach
вместо того , чтобы просто foreach
здесь
Path.GetInvalidFileNameChars().Aggregate(file, (current, c) => current.Replace(c, '-'))
Вот моя версия:
static string GetSafeFileName(string name, char replace = '_') {
char[] invalids = Path.GetInvalidFileNameChars();
return new string(name.Select(c => invalids.Contains(c) ? replace : c).ToArray());
}
Я не уверен, как рассчитывается результат GetInvalidFileNameChars, но «Get» предполагает, что это нетривиально, поэтому я кэширую результаты. Кроме того, при этом проходит входная строка только один раз, а не несколько раз, как в решениях выше, которые перебирают набор недопустимых символов, заменяя их в исходной строке по одному. Кроме того, мне нравятся решения на основе Where, но я предпочитаю заменять недопустимые символы вместо их удаления. Наконец, моя замена - это ровно один символ, чтобы избежать преобразования символов в строки, когда я перебираю строку.
Я говорю все это без профилирования - мне просто "показалось" приятным. :)
new HashSet<char>(Path.GetInvalidFileNameChars())
чтобы избежать перечисления O (n) - микрооптимизация.
Вот функция, которую я использую сейчас (спасибо jcollum за пример C #):
public static string MakeSafeFilename(string filename, char replaceChar)
{
foreach (char c in System.IO.Path.GetInvalidFileNameChars())
{
filename = filename.Replace(c, replaceChar);
}
return filename;
}
Я просто поместил это в класс «Помощники» для удобства.
Если вы хотите быстро удалить все специальные символы, которые иногда более удобочитаемы для имен файлов, это отлично работает:
string myCrazyName = "q`w^e!r@t#y$u%i^o&p*a(s)d_f-g+h=j{k}l|z:x\"c<v>b?n[m]q\\w;e'r,t.y/u";
string safeName = Regex.Replace(
myCrazyName,
"\W", /*Matches any nonword character. Equivalent to '[^A-Za-z0-9_]'*/
"",
RegexOptions.IgnoreCase);
// safeName == "qwertyuiopasd_fghjklzxcvbnmqwertyu"
\W
соответствует большему количеству не буквенно-цифровых ( [^A-Za-z0-9_]
). Все символы Unicode "word" (русский 中文 ... и т. Д.) Также не будут заменены. Но это хорошо.
.
поэтому вам нужно сначала извлечь расширение, а затем добавить его снова.
static class Utils
{
public static string MakeFileSystemSafe(this string s)
{
return new string(s.Where(IsFileSystemSafe).ToArray());
}
public static bool IsFileSystemSafe(char c)
{
return !Path.GetInvalidFileNameChars().Contains(c);
}
}
Почему бы не преобразовать строку в эквивалент Base64 следующим образом:
string UnsafeFileName = "salmnas dlajhdla kjha;dmas'lkasn";
string SafeFileName = Convert.ToBase64String(Encoding.UTF8.GetBytes(UnsafeFileName));
Если вы хотите преобразовать его обратно, чтобы вы могли его прочитать:
UnsafeFileName = Encoding.UTF8.GetString(Convert.FromBase64String(SafeFileName));
Я использовал это для сохранения файлов PNG с уникальным именем из случайного описания.
Вот что я только что добавил в статический класс StringExtensions ( http://github.com/Zoomicon/ClipFlair ) ClipFlair (проект Utils.Silverlight) на основе информации, собранной из ссылок на связанные вопросы о стеке, опубликованные Dour High Arch выше:
public static string ReplaceInvalidFileNameChars(this string s, string replacement = "")
{
return Regex.Replace(s,
"[" + Regex.Escape(new String(System.IO.Path.GetInvalidPathChars())) + "]",
replacement, //can even use a replacement string of any length
RegexOptions.IgnoreCase);
//not using System.IO.Path.InvalidPathChars (deprecated insecure API)
}
private void textBoxFileName_KeyPress(object sender, KeyPressEventArgs e)
{
e.Handled = CheckFileNameSafeCharacters(e);
}
/// <summary>
/// This is a good function for making sure that a user who is naming a file uses proper characters
/// </summary>
/// <param name="e"></param>
/// <returns></returns>
internal static bool CheckFileNameSafeCharacters(System.Windows.Forms.KeyPressEventArgs e)
{
if (e.KeyChar.Equals(24) ||
e.KeyChar.Equals(3) ||
e.KeyChar.Equals(22) ||
e.KeyChar.Equals(26) ||
e.KeyChar.Equals(25))//Control-X, C, V, Z and Y
return false;
if (e.KeyChar.Equals('\b'))//backspace
return false;
char[] charArray = Path.GetInvalidFileNameChars();
if (charArray.Contains(e.KeyChar))
return true;//Stop the character from being entered into the control since it is non-numerical
else
return false;
}
Я считаю, что это быстро и легко понять:
<Extension()>
Public Function MakeSafeFileName(FileName As String) As String
Return FileName.Where(Function(x) Not IO.Path.GetInvalidFileNameChars.Contains(x)).ToArray
End Function
Это работает, потому что a string
представляет IEnumerable
собой char
массив, и есть string
строка конструктора, которая принимает char
массив.
В своих старых проектах я нашел это решение, которое отлично работает более 2 лет. Я заменяю недопустимые символы на "!", А затем проверяю двойные !!, используйте свой собственный символ.
public string GetSafeFilename(string filename)
{
string res = string.Join("!", filename.Split(Path.GetInvalidFileNameChars()));
while (res.IndexOf("!!") >= 0)
res = res.Replace("!!", "!");
return res;
}
Многие ответы предлагают использовать, Path.GetInvalidFileNameChars()
что мне кажется плохим решением. Я рекомендую вам использовать белый список вместо черного, потому что хакеры всегда найдут способ в конечном итоге обойти его.
Вот пример кода, который вы можете использовать:
string whitelist = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.";
foreach (char c in filename)
{
if (!whitelist.Contains(c))
{
filename = filename.Replace(c, '-');
}
}