C # Dosya Adını Daralt


174

Son zamanlarda bir grup MP3'ü çeşitli konumlardan bir depoya taşıyorum. ID3 etiketlerini (teşekkürler, TagLib-Sharp!) Kullanarak yeni dosya adlarını inşa ediyordum ve ben bir System.NotSupportedException:

"Verilen yolun biçimi desteklenmiyor."

Bu ya File.Copy()veya tarafından oluşturuldu Directory.CreateDirectory().

Dosya adlarımın dezenfekte edilmesi gerektiğini anlamak uzun sürmedi. Açık olan şeyi yaptım:

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;
}

Şaşırtıcı bir şekilde, istisnalar almaya devam ettim. Path.GetInvalidPathChars()Bir yol kökünde geçerli olduğu için ':' setinde olmadığı ortaya çıktı . Sanırım bu mantıklı - ama bu oldukça yaygın bir sorun olmalı. Kimse yolu dezenfekte eden kısa bir kod var mı? En iyisi bunu buldum, ama muhtemelen aşırıya kaçmış gibi geliyor.

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

Bu işlevi daha hızlı ve daha az barok hale getirmek için yapılan iyileştirmeler çok takdir edilecektir.


Yanıtlar:


314

Bir dosya adını temizlemek için bunu yapabilirsiniz

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, "_" );
}

3
Soru, dosya adlarıyla değil yollarla ilgiliydi ve bunlar için geçersiz karakterler farklı.
Dour Yüksek Kemeri

15
Belki de, ama aynı sorun vardı bu kod kesinlikle bana yardımcı oldu :)
mmr

8
Ve potansiyel olarak harika bir SO kullanıcısı yürüyor ... Bu fonksiyon harika. Teşekkür ederim Adrevdm ...
Dan Rosenstark

19
Harika bir yöntem. Ayrılmış kelimelerin seni hala ısıracağını ve kafanı kaşıracağını unutma. Kaynak: Wikipedia Dosya adı saklıdır kelimeler
Spud

8
Periyotlar dosya adının sonundaysa geçersiz karakterdir, bu nedenle GetInvalidFileNameCharsbunları içermez. Pencerelerde bir istisna atmaz, sadece onları çıkarır, ancak dönemin orada olmasını bekliyorsanız beklenmedik davranışlara neden olabilir. Ben .dize sonunda ise geçersiz karakterlerden biri olarak kabul etmek için bu durumda işlemek için regex değiştirdi .
Scott Chamberlain

120

Daha kısa bir çözüm:

var invalids = System.IO.Path.GetInvalidFileNameChars();
var newName = String.Join("_", origFileName.Split(invalids, StringSplitOptions.RemoveEmptyEntries) ).TrimEnd('.');

1
@PeterMajeed: Satır sayımı sıfırdan başlar :-)
Gary McGill

Bu, özellikle platforma göre farklı karakterler döndürebilen ASP.NET Core için en iyi yanıttan daha iyidir.
Alexei

79

Andre'nin mükemmel cevabına dayanarak Spud'un ayrılmış kelimeler hakkındaki yorumunu dikkate alarak bu sürümü yaptım:

/// <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;
}

Bunlar benim birim testlerim

[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);
}

1
Bu, en azından sorunun dosya adı kısmına son derece eksiksiz bir cevaptır ve daha fazla oyu hak ediyor.
Brian MacKay

2
Yöntem bu yöne gidiyor gibi görünüyor çünkü küçük öneri: Bu anahtar kelimeyi ekleyin ve kullanışlı bir uzantı yöntemi haline gelir. public static String CoerceValidFileName (bu Dize dosya adı)
Ryan McArthur

2
Küçük hata: Bu yöntem, COM1izin verilmeyen dosya uzantıları (ör. ) Olmadan ayrılmış kelimeleri değiştirmez . Önerilen düzeltme, ayrılmışWordPattern'i "^{0}(\\.|$)"ve değiştirme dizesini"_reservedWord_$1"
Dehalion


4

System.IO.Path.GetInvalidFileNameChars() Geçersiz karakterleri kontrol etmek için yöntemi kullanıyorum ve hiçbir sorunum yok.

Aşağıdaki kodu kullanıyorum:

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

3

Karakterleri bir şekilde tutmak istedim, sadece karakteri bir alt çizgiyle değiştirmek değil.

Düşündüğüm bir yol, karakterleri (benim durumumda) normal karakterler olarak kullanılması muhtemel olmayan benzer karakterlerle değiştirmekti. Bu yüzden geçersiz karakterlerin listesini aldım ve benzer bir şey buldum.

Aşağıdakiler benzer görünümle kodlama ve kod çözme işlevleridir.

Bu kod, tüm System.IO.Path.GetInvalidFileNameChars () karakterleri için tam bir liste içermez. Bu nedenle, kalan karakterler için alt çizgi değiştirmeyi genişletmek veya kullanmak size kalmış.

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;
}

Kendi beğeneceğinizi seçebilirsiniz. Benimkini seçmek için Windows'ta Karakter Haritası uygulamasını kullandım%windir%\system32\charmap.exe

Keşif yoluyla ayarlamalar yaptığım için bu kodu güncelleyeceğim.


tam genişlik formu !"#$%&'()*+,-./:;<=>?@{|}~ veya /SOLIDUS ve `` FRACTION SLASH gibi diğer formları gibi karakterlere daha çok benzeyen birçok karakterin bulunduğunu unutmayın
sorunsuz

2

Bence sorun ilk önce Path.GetDirectoryNamekötü karakter dizisini çağırmanızdır . Dosya adında olmayan karakterler varsa .Net, dizenin hangi bölümlerinin dizin ve atar olduğunu söyleyemez. Dize karşılaştırmaları yapmanız gerekir.

Tüm yol değil, yalnızca kötü olan dosya adı olduğunu varsayarak şunu deneyin:

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

2

Geçmişte bununla başarılı oldum.

Güzel, kısa ve statik :-)

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

2

Burada birçok çalışma çözümü var. sadece tamlık uğruna, regex kullanmayan, ancak LINQ kullanan bir yaklaşım:

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

Ayrıca, çok kısa bir çözüm;)


1
I love one liners :)
Larry

1

Andre'nin koduna dayanan etkili bir tembel yükleme uzantısı yöntemi:

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, "_");
        }
    }
}

0

Dizini ve dosya adını ekleyip bağımsız olarak sterilize etmek yerine temizlediyseniz, kodunuz daha temiz olur. Dezenfekte etmek için:: dizedeki 2. karakteri alın. Eğer "replacechar" a eşitse, onu iki nokta üst üste işaretiyle değiştirin. Bu uygulama kendi kullanımınız olduğundan, böyle bir çözüm mükemmel bir şekilde yeterli olmalıdır.


-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();
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.