Есть ли способ сделать путь к файлу строки безопасным в C #? - PullRequest
82 голосов
/ 02 декабря 2008

Моя программа будет брать произвольные строки из Интернета и использовать их для имен файлов. Есть ли простой способ удалить плохие символы из этих строк или мне нужно написать специальную функцию для этого?

Ответы [ 13 ]

161 голосов
/ 02 декабря 2008

Тьфу, я ненавижу, когда люди пытаются угадать, какие символы действительны. Помимо того, что они были абсолютно непереносимыми (всегда думают о 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
31 голосов
/ 09 сентября 2010

Этот вопрос задавался много раз до и, как много раз указывалось ранее, IO.Path.GetInvalidFileNameChars не подходит.

Во-первых, есть много имен, таких как PRN и CON, которые зарезервированы и недопустимы для имен файлов. Другие имена не разрешены только в корневой папке. Имена, оканчивающиеся на точку, также не допускаются.

Во-вторых, существуют различные ограничения длины. Читайте полный список для NTFS здесь .

В-третьих, вы можете подключаться к файловым системам, имеющим другие ограничения. Например, имена файлов ISO 9660 не могут начинаться с «-», но могут содержать его.

В-четвертых, что вы делаете, если два процесса «произвольно» выбирают одно и то же имя?

В общем, использование сгенерированных извне имен для имен файлов - плохая идея. Я предлагаю генерировать ваши собственные личные имена файлов и хранить понятные человеку имена для внутреннего использования.

28 голосов
/ 09 сентября 2010

Чтобы удалить недопустимые символы:

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

Чтобы заменить недопустимые символы (и избежать потенциального конфликта имен, такого как Ад * против Ад $):

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());
21 голосов
/ 02 декабря 2008

Я согласен с 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));

p.s. - это более загадочно, чем должно быть, - я пытался быть кратким.

12 голосов
/ 20 сентября 2013

Вот моя версия:

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

Я говорю все, что без профилирования - это мне просто "понравилось". :)

10 голосов
/ 06 августа 2010

Вот функция, которую я сейчас использую (спасибо 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;
}

Я просто поместил это в класс "Помощников" для удобства.

6 голосов
/ 28 мая 2009

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

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"
5 голосов
/ 12 июля 2013

Вот что я только что добавил в статический класс ClipFlair (http://github.com/Zoomicon/ClipFlair) StringExtensions (проект 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)
}
5 голосов
/ 18 апреля 2013
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);
    }
}
4 голосов
/ 28 апреля 2017

Почему бы не преобразовать строку в эквивалент Base64 следующим образом:

string UnsafeFileName = "salmnas dlajhdla kjha;dmas'lkasn";
string SafeFileName = Convert.ToBase64String(Encoding.UTF8.GetBytes(UnsafeFileName));

Если вы хотите преобразовать его обратно, чтобы прочитать его:

UnsafeFileName = Encoding.UTF8.GetString(Convert.FromBase64String(SafeFileName));

Я использовал это для сохранения файлов PNG с уникальным именем из случайного описания.

...