Моя програма буде брати довільні рядки з Інтернету та використовувати їх для імен файлів. Чи існує простий спосіб видалити з цих рядків погані символи чи мені потрібно написати спеціальну функцію для цього?
Моя програма буде брати довільні рядки з Інтернету та використовувати їх для імен файлів. Чи існує простий спосіб видалити з цих рядків погані символи чи мені потрібно написати спеціальну функцію для цього?
Відповіді:
Тьфу, я ненавиджу, коли люди намагаються вгадати, які символи дійсні. Окрім того, що вони повністю не портативні (завжди думаючи про 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 не можуть починатися з "-", але можуть містити його.
По-четверте, що ви робите, якщо два процеси "довільно" вибирають одне і те ж ім'я?
Загалом, використання згенерованих зовні імен для імен файлів - погана ідея. Я пропоную згенерувати власні приватні імена файлів та зберігати зручні для читання імена всередині.
Я згоден з Grauenwolf і настійно рекомендую 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 з унікальним ім’ям із випадкового опису.
Ось те, що я щойно додав до статичного класу ClipFlair ( http://github.com/Zoomicon/ClipFlair ) StringExtensions (проект Utils.Silverlight) на основі інформації, зібраної за посиланнями на відповідні запитання щодо stackoverflow, розміщених 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
Це працює , тому що 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, '-');
}
}