Вопрос по sanitize, path, c#, validation, invalid-characters – C # Очистить имя файла

145

Недавно я переместил кучу MP3 из разных мест в хранилище. Я создавал новые имена файлов, используя теги ID3 (спасибо, TagLib-Sharp!), И я заметил, что получаюSystem.NotSupportedException:

Msgstr "Формат данного пути не поддерживается."

Это было сгенерировано либоFile.Copy() или жеDirectory.CreateDirectory().

Это не заняло много времени, чтобы понять, что мои имена файлов должны быть очищены. Итак, я сделал очевидную вещь:

public static string SanitizePath_(string path, char replaceChar)
{
    string dir = Path.GetDirectoryName(path);
    foreach (char c in Path.GetInvalidPathChars())
        dir = dir.Replace(c, replaceChar);

    string name = Path.GetFileName(path);
    foreach (char c in Path.GetInvalidFileNameChars())
        name = name.Replace(c, replaceChar);

    return dir + name;
}

К моему удивлению, я продолжал получать исключения. Оказалось, что «:» не входит в наборPath.GetInvalidPathChars(), потому что это действительно в корне пути. Я полагаю, это имеет смысл - но это должно быть довольно распространенной проблемой. У кого-нибудь есть какой-нибудь короткий код, который очищает путь? Самое тщательное, что я придумал, но такое чувство, что это, вероятно, излишне.

    // replaces invalid characters with replaceChar
    public static string SanitizePath(string path, char replaceChar)
    {
        // construct a list of characters that can't show up in filenames.
        // need to do this because ":" is not in InvalidPathChars
        if (_BadChars == null)
        {
            _BadChars = new List<char>(Path.GetInvalidFileNameChars());
            _BadChars.AddRange(Path.GetInvalidPathChars());
            _BadChars = Utility.GetUnique<char>(_BadChars);
        }

        // remove root
        string root = Path.GetPathRoot(path);
        path = path.Remove(0, root.Length);

        // split on the directory separator character. Need to do this
        // because the separator is not valid in a filename.
        List<string> parts = new List<string>(path.Split(new char[]{Path.DirectorySeparatorChar}));

        // check each part to make sure it is valid.
        for (int i = 0; i < parts.Count; i++)
        {
            string part = parts[i];
            foreach (char c in _BadChars)
            {
                part = part.Replace(c, replaceChar);
            }
            parts[i] = part;
        }

        return root + Utility.Join(parts, Path.DirectorySeparatorChar.ToString());
    }

Любые улучшения, чтобы сделать эту функцию более быстрой и менее барочной, будут высоко оценены.

Ваш Ответ

12   ответов
0

если вы добавите каталог и имя файла вместе и очистите их, а не очистите их самостоятельно. Что касается очистки от:, просто возьмите 2-й символ в строке. Если он равен «replacechar», замените его двоеточием. Поскольку это приложение для вашего собственного использования, такого решения должно быть вполне достаточно.

1

основанный на коде Андре:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LT
{
    public static class Utility
    {
        static string invalidRegStr;

        public static string MakeValidFileName(this string name)
        {
            if (invalidRegStr == null)
            {
                var invalidChars = System.Text.RegularExpressions.Regex.Escape(new string(System.IO.Path.GetInvalidFileNameChars()));
                invalidRegStr = string.Format(@"([{0}]*\.+$)|([{0}]+)", invalidChars);
            }

            return System.Text.RegularExpressions.Regex.Replace(name, invalidRegStr, "_");
        }
    }
}
277

вы можете сделать это

private static string MakeValidFileName( string name )
{
   string invalidChars = System.Text.RegularExpressions.Regex.Escape( new string( System.IO.Path.GetInvalidFileNameChars() ) );
   string invalidRegStr = string.Format( @"([{0}]*\.+$)|([{0}]+)", invalidChars );

   return System.Text.RegularExpressions.Regex.Replace( name, invalidRegStr, "_" );
}
Возможно, но этот код определенно помог мне, когда у меня была такая же проблема :) mmr
«Замечания: Массив, возвращаемый этим методом, не обязательно содержит полный набор символов, недопустимых в именах файлов и каталогов». Источник:Path.GetInvalidFileNameChars Метод Mark Byers
Отличный метод. Не забывайте, однако, что зарезервированные слова будут все еще кусать вас, и вы останетесь почесывая голову. Источник:Википедия Имя файла зарезервированные слова Spud
Точки являются недопустимыми символами, если они находятся в конце имени файла,GetInvalidFileNameChars не включает их. Он не генерирует исключение в окнах, он просто удаляет их, но может вызвать неожиданное поведение, если вы ожидаете, что период будет там. Я изменил регулярное выражение для обработки этого случая, чтобы вызвать. считается одним из недопустимых символов, если он находится в конце строки. Scott Chamberlain
4

System.IO.Path.GetInvalidFileNameChars() способ проверить недопустимые символы, и у меня нет проблем.

следующий код:

foreach( char invalidchar in System.IO.Path.GetInvalidFileNameChars())
{
    filename = filename.Replace(invalidchar, '_');
}
3

а не просто заменить символ подчеркиванием.

Один из способов, который я подумал, - заменить персонажей похожими персонажами, которые (в моей ситуации) вряд ли будут использоваться в качестве обычных символов. Таким образом, я взял список недопустимых символов и нашел, что они похожи.

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

Этот код не содержит полного списка всех символов System.IO.Path.GetInvalidFileNameChars (). Так что это зависит от вас, чтобы расширить или использовать замену подчеркивания для любых оставшихся символов.

private static Dictionary<string, string> EncodeMapping()
{
    //-- Following characters are invalid for windows file and folder names.
    //-- \/:*?"<>|
    Dictionary<string, string> dic = new Dictionary<string, string>();
    dic.Add(@"\", "Ì"); // U+OOCC
    dic.Add("/", "Í"); // U+OOCD
    dic.Add(":", "¦"); // U+00A6
    dic.Add("*", "¤"); // U+00A4
    dic.Add("?", "¿"); // U+00BF
    dic.Add(@"""", "ˮ"); // U+02EE
    dic.Add("<", "«"); // U+00AB
    dic.Add(">", "»"); // U+00BB
    dic.Add("|", "│"); // U+2502
    return dic;
}

public static string Escape(string name)
{
    foreach (KeyValuePair<string, string> replace in EncodeMapping())
    {
        name = name.Replace(replace.Key, replace.Value);
    }

    //-- handle dot at the end
    if (name.EndsWith(".")) name = name.CropRight(1) + "°";

    return name;
}

public static string UnEscape(string name)
{
    foreach (KeyValuePair<string, string> replace in EncodeMapping())
    {
        name = name.Replace(replace.Value, replace.Key);
    }

    //-- handle dot at the end
    if (name.EndsWith("°")) name = name.CropRight(1) + ".";

    return name;
}

Вы можете выбрать свой собственный внешний вид. Я использовал приложение Карта персонажей в Windows, чтобы выбрать мое%windir%\system32\charmap.exe

Поскольку я делаю настройки через открытие, я обновлю этот код.

обратите внимание, что есть много символов, которые выглядят более похожими на те, какполная форма !"#$%&'()*+,-./:;<=>?@{|}~ или другие формы, такие как/ SOLIDUS и `⁄` FRACTION SLASH, которые можно без проблем использовать непосредственно в именах файлов phuclv
2

Path.GetDirectoryName на плохой строке. Если в нем есть символы не из имен файлов, .Net не может определить, какие части строки являются каталогами и выбрасывает. Вы должны сделать сравнение строк.

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

public static string SanitizePath(string path, char replaceChar)
{
    int filenamePos = path.LastIndexOf(Path.DirectorySeparatorChar) + 1;
    var sb = new System.Text.StringBuilder();
    sb.Append(path.Substring(0, filenamePos));
    for (int i = filenamePos; i < path.Length; i++)
    {
        char filenameChar = path[i];
        foreach (char c in Path.GetInvalidFileNameChars())
            if (filenameChar.Equals(c))
            {
                filenameChar = replaceChar;
                break;
            }

        sb.Append(filenameChar);
    }

    return sb.ToString();
}
1

вот подход, который не использует регулярные выражения, но использует LINQ:

var invalids = Path.GetInvalidFileNameChars();
filename = invalids.Aggregate(filename, (current, c) => current.Replace(c, '_'));

Кроме того, это очень короткое решение;)

66

но принимая во внимание комментарий Спуда к зарезервированным словам, я сделал эту версию:

/// <summary>
/// Strip illegal chars and reserved words from a candidate filename (should not include the directory path)
/// </summary>
/// <remarks>
/// http://stackoverflow.com/questions/309485/c-sharp-sanitize-file-name
/// </remarks>
public static string CoerceValidFileName(string filename)
{
    var invalidChars = Regex.Escape(new string(Path.GetInvalidFileNameChars()));
    var invalidReStr = string.Format(@"[{0}]+", invalidChars);

    var reservedWords = new []
    {
        "CON", "PRN", "AUX", "CLOCK$", "NUL", "COM0", "COM1", "COM2", "COM3", "COM4",
        "COM5", "COM6", "COM7", "COM8", "COM9", "LPT0", "LPT1", "LPT2", "LPT3", "LPT4",
        "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"
    };

    var sanitisedNamePart = Regex.Replace(filename, invalidReStr, "_");
    foreach (var reservedWord in reservedWords)
    {
        var reservedWordPattern = string.Format("^{0}\\.", reservedWord);
        sanitisedNamePart = Regex.Replace(sanitisedNamePart, reservedWordPattern, "_reservedWord_.", RegexOptions.IgnoreCase);
    }

    return sanitisedNamePart;
}

И это мои юнит-тесты

[Test]
public void CoerceValidFileName_SimpleValid()
{
    var filename = @"thisIsValid.txt";
    var result = PathHelper.CoerceValidFileName(filename);
    Assert.AreEqual(filename, result);
}

[Test]
public void CoerceValidFileName_SimpleInvalid()
{
    var filename = @"thisIsNotValid\3\\_3.txt";
    var result = PathHelper.CoerceValidFileName(filename);
    Assert.AreEqual("thisIsNotValid_3__3.txt", result);
}

[Test]
public void CoerceValidFileName_InvalidExtension()
{
    var filename = @"thisIsNotValid.t\xt";
    var result = PathHelper.CoerceValidFileName(filename);
    Assert.AreEqual("thisIsNotValid.t_xt", result);
}

[Test]
public void CoerceValidFileName_KeywordInvalid()
{
    var filename = "aUx.txt";
    var result = PathHelper.CoerceValidFileName(filename);
    Assert.AreEqual("_reservedWord_.txt", result);
}

[Test]
public void CoerceValidFileName_KeywordValid()
{
    var filename = "auxillary.txt";
    var result = PathHelper.CoerceValidFileName(filename);
    Assert.AreEqual("auxillary.txt", result);
}
ta - орфография исправлена fiat
Спасибо за акцию / тесты! Также: тривиальное написание заменить:reserverd -> reserved el2iot2
Это очень полный ответ, по крайней мере, на часть вопроса, касающуюся имени файла, и заслуживает большего количества голосов. Brian MacKay
Небольшая ошибка: этот метод не изменяет зарезервированные слова без расширений файлов (например,COM1), которые также запрещены. Предложенное исправление будет состоять в том, чтобы изменить зарезервированныйWordPattern на"^{0}(\\.|$)" и замена строки в"_reservedWord_$1" Dehalion
Незначительное предложение, так как похоже, что метод идет в этом направлении: добавьте ключевое слово this, и он станет удобным методом расширения. public static String CoerceValidFileName (это имя файла String) Ryan McArthur
-1
using System;
using System.IO;
using System.Linq;
using System.Text;

public class Program
{
    public static void Main()
    {
        try
        {
            var badString = "ABC\\DEF/GHI<JKL>MNO:PQR\"STU\tVWX|YZA*BCD?EFG";
            Console.WriteLine(badString);
            Console.WriteLine(SanitizeFileName(badString, '.'));
            Console.WriteLine(SanitizeFileName(badString));
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
    }

    private static string SanitizeFileName(string fileName, char? replacement = null)
    {
        if (fileName == null) { return null; }
        if (fileName.Length == 0) { return ""; }

        var sb = new StringBuilder();
        var badChars = Path.GetInvalidFileNameChars().ToList();

        foreach (var @char in fileName)
        {
            if (badChars.Contains(@char)) 
            {
                if (replacement.HasValue)
                {
                    sb.Append(replacement.Value);
                }
                continue; 
            }
            sb.Append(@char);
        }
        return sb.ToString();
    }
}
95

var invalids = System.IO.Path.GetInvalidFileNameChars();
var newName = String.Join("_", origFileName.Split(invalids, StringSplitOptions.RemoveEmptyEntries) ).TrimEnd('.');
Это лучше, чем топовый ответ, особенно для ASP.NET Core, который может возвращать разные символы в зависимости от платформы. Alexei
@PeterMajeed: TIL, что подсчет строк начинается с нуля :-) Gary McGill
28
string clean = String.Concat(dirty.Split(Path.GetInvalidFileNameChars()));
рассматриватьString.Concat(dirty...) вместоJoin(String.Empty... drzaus
DenNukem уже предложил этот ответ:stackoverflow.com/a/13617375/244916 (то же самое рассмотрите комментарий, все же). Dude Pascalou
2

Хороший, короткий и статичный :-)

    public static string returnSafeString(string s)
    {
        foreach (char character in Path.GetInvalidFileNameChars())
        {
            s = s.Replace(character.ToString(),string.Empty);
        }

        foreach (char character in Path.GetInvalidPathChars())
        {
            s = s.Replace(character.ToString(), string.Empty);
        }

        return (s);
    }

Похожие вопросы