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



У меня есть строка типа "Foo: Bar", которую я хочу использовать в качестве имени файла, но в Windows ":" char не допускается в имени файла.



есть ли метод, который превратит "Foo: Bar "в нечто вроде"Foo - Bar"?

597   12  

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

Да, это действительно работает:

Explorer sample

нюанс Риски

Я знал, что этот трюк будет работать на 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

    Ничего не найдено.