Bagaimana cara menghapus karakter ilegal dari jalur dan nama file?

456

Saya perlu cara yang kuat dan sederhana untuk menghapus jalur ilegal dan karakter file dari string sederhana. Saya telah menggunakan kode di bawah ini tetapi sepertinya tidak melakukan apa-apa, apa yang saya lewatkan?

using System;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string illegal = "\"M<>\"\\a/ry/ h**ad:>> a\\/:*?\"<>| li*tt|le|| la\"mb.?";

            illegal = illegal.Trim(Path.GetInvalidFileNameChars());
            illegal = illegal.Trim(Path.GetInvalidPathChars());

            Console.WriteLine(illegal);
            Console.ReadLine();
        }
    }
}
Gary Willoughby
sumber
1
Potong menghapus karakter dari awal dan akhir string. Namun, Anda mungkin harus bertanya mengapa data tidak valid, dan alih-alih mencoba dan membersihkan / memperbaiki data, tolak data tersebut.
user7116
8
Nama gaya Unix tidak valid pada Windows dan saya tidak ingin berurusan dengan nama pendek 8.3.
Gary Willoughby
GetInvalidFileNameChars()akan menghapus hal-hal seperti: \ etc dari path folder.
CAD berbicara
1
Path.GetInvalidPathChars()tampaknya tidak menelanjangi *atau?
CAD cowok
19
Saya menguji lima jawaban dari pertanyaan ini (rentang waktu 100.000) dan metode berikut adalah yang tercepat. Ekspresi reguler mengambil tempat ke-2, dan 25% lebih lambat: public string GetSafeFilename (string filename) {return string.Join ("_", filename.Split (Path.GetInvalidFileNameChars ())); }
Brain2000

Jawaban:

494

Cobalah sesuatu seperti ini sebagai gantinya;

string illegal = "\"M\"\\a/ry/ h**ad:>> a\\/:*?\"| li*tt|le|| la\"mb.?";
string invalid = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());

foreach (char c in invalid)
{
    illegal = illegal.Replace(c.ToString(), ""); 
}

Tapi saya harus setuju dengan komentar, saya mungkin akan mencoba untuk berurusan dengan sumber jalur ilegal, daripada mencoba untuk memotong jalan ilegal ke jalur yang sah tapi mungkin tidak diinginkan.

Sunting: Atau solusi yang berpotensi 'lebih baik', menggunakan Regex.

string illegal = "\"M\"\\a/ry/ h**ad:>> a\\/:*?\"| li*tt|le|| la\"mb.?";
string regexSearch = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
Regex r = new Regex(string.Format("[{0}]", Regex.Escape(regexSearch)));
illegal = r.Replace(illegal, "");

Namun, pertanyaannya tetap harus ditanyakan, mengapa Anda melakukan ini sejak awal.

Matthew Scharley
sumber
40
Tidak perlu menambahkan kedua daftar bersama. Daftar char nama file ilegal berisi daftar char path ilegal dan memiliki beberapa lagi. Berikut adalah daftar dari kedua daftar yang dimasukkan ke int: 34,60,62,124,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,58,42,63,92,47 34,60,62,124,0,1,2 , 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27 , 28,29,30,31
Sarel Botha
9
@ sjbotha ini mungkin benar pada Windows dan implementasi Microsoft. NET. Saya tidak mau membuat asumsi yang sama untuk mengatakan mono menjalankan Linux.
Matthew Scharley
7
Mengenai solusi pertama. Bukankah seharusnya StringBuilder lebih efisien daripada penugasan string?
epignosisx
6
Untuk apa nilainya, @MatthewScharley, implementasi Mono dari GetInvalidPathChars () mengembalikan hanya 0x00 dan GetInvalidFileNameChars () mengembalikan hanya 0x00 dan '/' ketika berjalan pada platform non-Windows. Di Windows, daftar karakter yang tidak valid jauh lebih lama, dan GetInvalidPathChars () seluruhnya diduplikasi di dalam GetInvalidFileNameChars (). Ini tidak akan berubah di masa mendatang, jadi semua yang Anda lakukan sebenarnya adalah menggandakan jumlah waktu yang dibutuhkan fungsi ini untuk berjalan karena Anda khawatir definisi jalur yang valid akan berubah dalam waktu dekat. Yang mana tidak.
Warren Rumak
13
@Charleh diskusi ini sangat tidak perlu ... kode harus selalu dioptimalkan dan tidak ada risiko ini salah. Nama file juga bagian dari jalur. Jadi tidak masuk akal kalau GetInvalidPathChars()bisa mengandung karakter yang GetInvalidFileNameChars()tidak. Anda tidak mengambil kebenaran dari optimasi "prematur". Anda hanya menggunakan kode yang salah.
Stefan Fabian
355

Pertanyaan awal yang diminta untuk "menghapus karakter ilegal":

public string RemoveInvalidChars(string filename)
{
    return string.Concat(filename.Split(Path.GetInvalidFileNameChars()));
}

Anda mungkin ingin menggantinya:

public string ReplaceInvalidChars(string filename)
{
    return string.Join("_", filename.Split(Path.GetInvalidFileNameChars()));    
}

Jawaban ini ada di utas lain oleh Ceres , saya sangat suka rapi dan sederhana.

Shehab Fawzy
sumber
10
Untuk menjawab pertanyaan OP secara tepat, Anda perlu menggunakan "" alih-alih "_", tetapi jawaban Anda mungkin berlaku untuk lebih banyak dari kita dalam praktik. Saya pikir mengganti karakter ilegal dengan yang legal lebih umum dilakukan.
BH
37
Saya menguji lima metode dari pertanyaan ini (waktunya 100.000) dan metode ini adalah yang tercepat. Ekspresi reguler mengambil tempat ke-2, dan 25% lebih lambat dari metode ini.
Brain2000
10
Untuk menanggapi komentar @BH, seseorang cukup menggunakan string.Concat (name.Split (Path.GetInvalidFileNameChars ()))
Michael Sutton
210

Saya menggunakan Linq untuk membersihkan nama file. Anda dapat dengan mudah memperluas ini untuk memeriksa jalur yang valid juga.

private static string CleanFileName(string fileName)
{
    return Path.GetInvalidFileNameChars().Aggregate(fileName, (current, c) => current.Replace(c.ToString(), string.Empty));
}

Memperbarui

Beberapa komentar menunjukkan metode ini tidak berfungsi untuk mereka, jadi saya telah menyertakan tautan ke cuplikan DotNetFiddle sehingga Anda dapat memvalidasi metode ini.

https://dotnetfiddle.net/nw1SWY

Michael Minton
sumber
4
Ini tidak berhasil untuk saya. Metode ini tidak mengembalikan string bersih. Itu mengembalikan nama file yang lewat apa adanya.
Karan
Apa yang dikatakan @Karan, ini tidak berfungsi, string asli kembali.
Jon
Anda benar-benar dapat melakukan ini dengan Linq seperti ini meskipun: var invalid = new HashSet<char>(Path.GetInvalidPathChars()); return new string(originalString.Where(s => !invalid.Contains(s)).ToArray()). Kinerja mungkin tidak bagus, tetapi itu mungkin tidak masalah.
Casey
2
@Karan atau Jon Input apa yang Anda kirimkan fungsi ini? Lihat hasil edit saya untuk verifikasi metode ini.
Michael Minton
3
Sangat mudah - orang-orang memberikan string dengan karakter yang valid. Terpilih untuk solusi Agregat keren.
Nickmaovich
89

Anda dapat menghapus karakter ilegal menggunakan Linq seperti ini:

var invalidChars = Path.GetInvalidFileNameChars();

var invalidCharsRemoved = stringWithInvalidChars
.Where(x => !invalidChars.Contains(x))
.ToArray();

EDIT
Ini adalah tampilannya dengan pengeditan yang diperlukan yang disebutkan dalam komentar:

var invalidChars = Path.GetInvalidFileNameChars();

string invalidCharsRemoved = new string(stringWithInvalidChars
  .Where(x => !invalidChars.Contains(x))
  .ToArray());
Gregor Slavec
sumber
1
Saya suka cara ini: Anda hanya menyimpan karakter yang diizinkan dalam string (yang tidak lain adalah array char).
Dude Pascalou
6
Saya tahu bahwa ini adalah pertanyaan lama, tetapi ini adalah jawaban yang luar biasa. Namun, saya ingin menambahkan bahwa dalam c # Anda tidak dapat menggunakan dari char [] ke string baik secara implisit atau eksplisit (gila, saya tahu) sehingga Anda harus memasukkannya ke string constructor.
JNYRanger
1
Saya belum mengkonfirmasi ini, tapi saya berharap Path.GetInvalidPathChars () menjadi superset dari GetInvalidFileNameChars () dan untuk mencakup kedua nama file dan jalur, jadi saya mungkin akan menggunakannya.
angularsen
3
@anjdreas sebenarnya Path.GetInvalidPathChars () tampaknya merupakan bagian dari Path.GetInvalidFileNameChars (), bukan sebaliknya. Path.GetInvalidPathChars () tidak akan mengembalikan '?', Misalnya.
Rafael Costa
1
Ini jawaban yang bagus. Saya menggunakan daftar nama file dan daftar filepath: ____________________________ string cleanData = string baru (data.Where (x =>! Path.GetInvalidFileNameChars (). Berisi (x) &&! Path.GetInvalidPathChars (). Berisi (x)). ToArray ());
goamn
27

Ini semua adalah solusi hebat, tetapi mereka semua bergantung Path.GetInvalidFileNameChars, yang mungkin tidak dapat diandalkan seperti yang Anda pikirkan. Perhatikan komentar berikut dalam dokumentasi MSDN pada Path.GetInvalidFileNameChars:

Array yang dikembalikan dari metode ini tidak dijamin mengandung set karakter lengkap yang tidak valid dalam nama file dan direktori. Set lengkap karakter yang tidak valid dapat bervariasi menurut sistem file. Misalnya, pada platform desktop berbasis Windows, karakter jalur yang tidak valid dapat mencakup karakter ASCII / Unicode 1 hingga 31, serta kutipan ("), kurang dari (<), lebih besar dari (>), pipa (|), backspace ( \ b), null (\ 0) dan tab (\ t).

Ini tidak lebih baik dengan Path.GetInvalidPathCharsmetode. Ini berisi komentar yang sama persis.

René
sumber
13
Lalu apa gunanya Path.GetInvalidFileNameChars? Saya berharap untuk mengembalikan karakter yang tidak benar untuk sistem saat ini, bergantung pada .NET untuk mengetahui sistem file yang saya jalankan dan menyajikan kepada saya karakter yang tidak sesuai. Jika ini bukan kasusnya dan itu hanya mengembalikan karakter hardcoded, yang tidak dapat diandalkan di tempat pertama, metode ini harus dihapus karena memiliki nilai nol.
Jan
1
Saya tahu ini adalah komentar lama tetapi, @Jan Anda ingin menulis di filesystem lain, mungkin ini sebabnya ada peringatan.
fantastik78
3
@ fantastik78 poin bagus, tetapi dalam hal ini saya ingin memiliki argumen enum tambahan untuk menentukan FS jarak jauh saya. Jika ini terlalu banyak upaya pemeliharaan (yang kemungkinan besar terjadi), seluruh metode ini masih merupakan ide yang buruk, karena memberi Anda kesan keselamatan yang salah.
Jan
1
@ Jan Saya benar-benar setuju dengan Anda, saya hanya berdebat tentang peringatan itu.
fantastik78
Menariknya ini adalah semacam "daftar hitam" karakter yang tidak valid. Bukankah lebih baik "daftar putih" hanya karakter valid yang dikenal di sini ?! Mengingatkan saya pada ide "virusscanner" bodoh daripada memasukkan aplikasi yang dibolehkan masuk daftar putih ....
Bernhard
26

Untuk nama file:

var cleanFileName = string.Join("", fileName.Split(Path.GetInvalidFileNameChars()));

Untuk jalur lengkap:

var cleanPath = string.Join("", path.Split(Path.GetInvalidPathChars()));

Perhatikan bahwa jika Anda bermaksud menggunakan ini sebagai fitur keamanan, pendekatan yang lebih kuat adalah dengan memperluas semua jalur dan kemudian memverifikasi bahwa jalur yang disediakan pengguna memang merupakan anak dari direktori yang harus diakses oleh pengguna.

Lily Finley
sumber
18

Sebagai permulaan, Trim hanya menghilangkan karakter dari awal atau akhir string . Kedua, Anda harus mengevaluasi apakah Anda benar-benar ingin menghapus karakter ofensif, atau gagal cepat dan biarkan pengguna tahu nama file mereka tidak valid. Pilihan saya adalah yang terakhir, tetapi jawaban saya setidaknya harus menunjukkan kepada Anda bagaimana melakukan sesuatu dengan cara yang benar DAN salah:

Pertanyaan StackOverflow menunjukkan bagaimana memeriksa apakah string yang diberikan adalah nama file yang valid . Catatan Anda dapat menggunakan regex dari pertanyaan ini untuk menghapus karakter dengan penggantian ekspresi reguler (jika Anda benar-benar perlu melakukan ini).

pengguna7116
sumber
Saya terutama setuju dengan saran kedua.
OregonGhost
4
Saya biasanya setuju dengan yang kedua, tetapi saya memiliki program yang menghasilkan nama file dan yang mungkin mengandung karakter ilegal dalam beberapa situasi. Karena program saya menghasilkan nama file ilegal, saya pikir pantas untuk menghapus / mengganti karakter tersebut. (Hanya menunjukkan case-use yang valid)
JDB masih mengingat Monica
16

Cara terbaik untuk menghapus karakter ilegal dari input pengguna adalah mengganti karakter ilegal menggunakan kelas Regex, membuat metode dalam kode di belakang atau juga memvalidasi di sisi klien menggunakan kontrol RegularExpression.

public string RemoveSpecialCharacters(string str)
{
    return Regex.Replace(str, "[^a-zA-Z0-9_]+", "_", RegexOptions.Compiled);
}

ATAU

<asp:RegularExpressionValidator ID="regxFolderName" 
                                runat="server" 
                                ErrorMessage="Enter folder name with  a-z A-Z0-9_" 
                                ControlToValidate="txtFolderName" 
                                Display="Dynamic" 
                                ValidationExpression="^[a-zA-Z0-9_]*$" 
                                ForeColor="Red">
anomepani
sumber
5
IMHO solusi ini jauh lebih baik daripada yang lain Alih-alih mencari semua karakter yang tidak valid, tentukan saja yang valid.
igorushi
15

Saya menggunakan ekspresi reguler untuk mencapai ini. Pertama, saya secara dinamis membangun regex.

string regex = string.Format(
                   "[{0}]",
                   Regex.Escape(new string(Path.GetInvalidFileNameChars())));
Regex removeInvalidChars = new Regex(regex, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant);

Kemudian saya panggil removeInvalidChars.Replace untuk melakukan pencarian dan penggantian. Ini jelas dapat diperluas untuk mencakup karakter jalur juga.

Jeff Yates
sumber
Aneh, itu berhasil untuk saya. Saya akan mengeceknya saat saya mendapat kesempatan. Bisakah Anda lebih spesifik dan menjelaskan apa yang sebenarnya tidak berhasil untuk Anda?
Jeff Yates
1
Ini tidak akan berfungsi (setidaknya dengan benar) karena Anda tidak melarikan diri karakter jalur dengan benar, dan beberapa dari mereka memiliki makna khusus. Lihat jawaban saya untuk cara melakukannya.
Matthew Scharley
@ Jeff: Versi Anda masih lebih baik daripada Matthew, jika Anda sedikit memodifikasinya. Lihat jawaban saya tentang caranya.
Jan
2
Saya juga akan menambahkan beberapa pola nama file tidak valid lainnya yang dapat ditemukan di MSDN dan memperluas solusi Anda ke regex berikut:new Regex(String.Format("^(CON|PRN|AUX|NUL|CLOCK\$|COM[1-9]|LPT[1-9])(?=\..|$)|(^(\.+|\s+)$)|((\.+|\s+)$)|([{0}])", Regex.Escape(new String(Path.GetInvalidFileNameChars()))), RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.CultureInvariant);
yar_shukan
13

Saya benar-benar menyukai gagasan Jeff Yates. Ini akan bekerja dengan sempurna, jika Anda sedikit memodifikasinya:

string regex = String.Format("[{0}]", Regex.Escape(new string(Path.GetInvalidFileNameChars())));
Regex removeInvalidChars = new Regex(regex, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant);

Peningkatan ini hanya untuk menghindari regex yang dibuat secara otomatis.

Jan
sumber
11

Berikut cuplikan kode yang akan membantu untuk .NET 3 dan lebih tinggi.

using System.IO;
using System.Text.RegularExpressions;

public static class PathValidation
{
    private static string pathValidatorExpression = "^[^" + string.Join("", Array.ConvertAll(Path.GetInvalidPathChars(), x => Regex.Escape(x.ToString()))) + "]+$";
    private static Regex pathValidator = new Regex(pathValidatorExpression, RegexOptions.Compiled);

    private static string fileNameValidatorExpression = "^[^" + string.Join("", Array.ConvertAll(Path.GetInvalidFileNameChars(), x => Regex.Escape(x.ToString()))) + "]+$";
    private static Regex fileNameValidator = new Regex(fileNameValidatorExpression, RegexOptions.Compiled);

    private static string pathCleanerExpression = "[" + string.Join("", Array.ConvertAll(Path.GetInvalidPathChars(), x => Regex.Escape(x.ToString()))) + "]";
    private static Regex pathCleaner = new Regex(pathCleanerExpression, RegexOptions.Compiled);

    private static string fileNameCleanerExpression = "[" + string.Join("", Array.ConvertAll(Path.GetInvalidFileNameChars(), x => Regex.Escape(x.ToString()))) + "]";
    private static Regex fileNameCleaner = new Regex(fileNameCleanerExpression, RegexOptions.Compiled);

    public static bool ValidatePath(string path)
    {
        return pathValidator.IsMatch(path);
    }

    public static bool ValidateFileName(string fileName)
    {
        return fileNameValidator.IsMatch(fileName);
    }

    public static string CleanPath(string path)
    {
        return pathCleaner.Replace(path, "");
    }

    public static string CleanFileName(string fileName)
    {
        return fileNameCleaner.Replace(fileName, "");
    }
}
James
sumber
8

Sebagian besar solusi di atas menggabungkan karakter ilegal untuk path dan nama file yang salah (bahkan ketika kedua panggilan saat ini mengembalikan kumpulan karakter yang sama). Saya pertama-tama akan membagi path + nama file di jalur dan nama file, kemudian menerapkan set yang sesuai baik jika mereka dan kemudian menggabungkan keduanya lagi.

wvd_vegt

wvd_vegt
sumber
+1: Sangat benar. Hari ini, bekerja di .NET 4.0, solusi regex dari jawaban teratas menghapus semua garis miring terbalik di jalur penuh. Jadi saya membuat regex untuk path dir dan regex hanya untuk nama file, dibersihkan secara terpisah dan digabungkan kembali
dario_ramos
Itu mungkin benar tetapi ini tidak menjawab pertanyaan. Saya tidak yakin samar-samar 'Saya akan melakukannya seperti ini' sangat membantu dibandingkan dengan beberapa solusi lengkap yang sudah ada di sini (lihat misalnya jawaban Lilly, di bawah)
Ian Grainger
6

Jika Anda menghapus atau mengganti satu karakter dengan karakter yang tidak valid, Anda dapat mengalami tabrakan:

<abc -> abc
>abc -> abc

Berikut adalah metode sederhana untuk menghindari ini:

public static string ReplaceInvalidFileNameChars(string s)
{
    char[] invalidFileNameChars = System.IO.Path.GetInvalidFileNameChars();
    foreach (char c in invalidFileNameChars)
        s = s.Replace(c.ToString(), "[" + Array.IndexOf(invalidFileNameChars, c) + "]");
    return s;
}

Hasil:

 <abc -> [1]abc
 >abc -> [2]abc
Maxence
sumber
5

Lempar pengecualian.

if ( fileName.IndexOfAny(Path.GetInvalidFileNameChars()) > -1 )
            {
                throw new ArgumentException();
            }
mirezus
sumber
4

Saya menulis monster ini untuk bersenang-senang, itu memungkinkan Anda pulang pergi:

public static class FileUtility
{
    private const char PrefixChar = '%';
    private static readonly int MaxLength;
    private static readonly Dictionary<char,char[]> Illegals;
    static FileUtility()
    {
        List<char> illegal = new List<char> { PrefixChar };
        illegal.AddRange(Path.GetInvalidFileNameChars());
        MaxLength = illegal.Select(x => ((int)x).ToString().Length).Max();
        Illegals = illegal.ToDictionary(x => x, x => ((int)x).ToString("D" + MaxLength).ToCharArray());
    }

    public static string FilenameEncode(string s)
    {
        var builder = new StringBuilder();
        char[] replacement;
        using (var reader = new StringReader(s))
        {
            while (true)
            {
                int read = reader.Read();
                if (read == -1)
                    break;
                char c = (char)read;
                if(Illegals.TryGetValue(c,out replacement))
                {
                    builder.Append(PrefixChar);
                    builder.Append(replacement);
                }
                else
                {
                    builder.Append(c);
                }
            }
        }
        return builder.ToString();
    }

    public static string FilenameDecode(string s)
    {
        var builder = new StringBuilder();
        char[] buffer = new char[MaxLength];
        using (var reader = new StringReader(s))
        {
            while (true)
            {
                int read = reader.Read();
                if (read == -1)
                    break;
                char c = (char)read;
                if (c == PrefixChar)
                {
                    reader.Read(buffer, 0, MaxLength);
                    var encoded =(char) ParseCharArray(buffer);
                    builder.Append(encoded);
                }
                else
                {
                    builder.Append(c);
                }
            }
        }
        return builder.ToString();
    }

    public static int ParseCharArray(char[] buffer)
    {
        int result = 0;
        foreach (char t in buffer)
        {
            int digit = t - '0';
            if ((digit < 0) || (digit > 9))
            {
                throw new ArgumentException("Input string was not in the correct format");
            }
            result *= 10;
            result += digit;
        }
        return result;
    }
}
Johan Larsson
sumber
1
Saya suka ini karena ia menghindari dua string berbeda yang menciptakan jalur hasil yang sama.
Kim
3

Saya pikir jauh lebih mudah untuk memvalidasi menggunakan regex dan menentukan karakter mana yang diperbolehkan, daripada mencoba memeriksa semua karakter buruk. Lihat tautan ini: http://www.c-sharpcorner.com/UploadFile/prasad_1/RegExpPSD12062005021717AM/RegExpPSD.aspx http://www.windowsdevcenter.com/pub/a/oreilly/windows/news/csharp_0101.html

Juga, lakukan pencarian untuk "editor ekspresi reguler", mereka banyak membantu. Ada beberapa yang bahkan mengeluarkan kode dalam c # untuk Anda.

Sandor Davidhazi
sumber
Mengingat bahwa .net adalah kerangka kerja yang dimaksudkan untuk memungkinkan program berjalan pada banyak platform (mis. Linux / Unix dan juga Windows), saya merasa Path.GetInvalidFileNameChars () adalah yang terbaik karena akan berisi pengetahuan tentang apa yang ada atau tidak. t valid pada sistem file yang sedang dijalankan oleh program Anda. Bahkan jika program Anda tidak akan berjalan di Linux (mungkin penuh dengan kode WPF), selalu ada kemungkinan beberapa sistem file Windows baru akan datang di masa depan dan memiliki karakter yang valid / tidak valid berbeda. Menggulirkan sendiri dengan regex adalah menciptakan kembali roda, dan memindahkan masalah platform ke kode Anda sendiri.
Daniel Scott
Saya setuju dengan saran Anda tentang editor / penguji regex online. Saya menemukan mereka sangat berharga (karena regex adalah hal yang rumit, dan penuh kehalusan yang dapat membuat Anda mudah tersandung, memberi Anda regex yang berperilaku dengan cara yang sangat tidak terduga dengan case edge). Favorit saya adalah regex101.com (Saya suka bagaimana itu memecah regex ke bawah dan menunjukkan dengan jelas apa yang diharapkan cocok). Saya juga cukup suka debuggex.com karena punya representasi visual yang kompak dari grup pertandingan dan kelas karakter dan yang lainnya.
Daniel Scott
3

Ini tampaknya O (n) dan tidak menghabiskan terlalu banyak memori pada string:

    private static readonly HashSet<char> invalidFileNameChars = new HashSet<char>(Path.GetInvalidFileNameChars());

    public static string RemoveInvalidFileNameChars(string name)
    {
        if (!name.Any(c => invalidFileNameChars.Contains(c))) {
            return name;
        }

        return new string(name.Where(c => !invalidFileNameChars.Contains(c)).ToArray());
    }
Alexey F
sumber
1
Saya tidak berpikir itu O (n) ketika Anda menggunakan fungsi 'Any'.
II PANAH
@IIARROWS dan apa pendapat Anda?
Alexey F
Saya tidak tahu, rasanya tidak seperti itu ketika saya menulis komentar saya ... sekarang saya mencoba menghitungnya, sepertinya Anda benar.
II PANAH
Saya memilih ini karena pertimbangan kinerja Anda. Terima kasih.
Berend Engelbrecht
3

Memindai jawaban di sini, semuanya ** tampaknya melibatkan penggunaan array karakter karakter nama file yang tidak valid.

Memang, ini mungkin mikro-optimasi - tetapi untuk kepentingan siapa pun yang mungkin mencari untuk memeriksa sejumlah besar nilai untuk menjadi nama file yang valid, perlu dicatat bahwa membangun hashset karakter yang tidak valid akan menghasilkan kinerja yang jauh lebih baik.

Saya sangat terkejut (terkejut) di masa lalu seberapa cepat hashset (atau kamus) mengungguli iterasi pada daftar. Dengan string, ini adalah angka yang sangat rendah (sekitar 5-7 item dari memori). Dengan sebagian besar data sederhana lainnya (referensi objek, angka, dll), crossover ajaib tampaknya ada sekitar 20 item.

Ada 40 karakter yang tidak valid di "daftar" Path.InvalidFileNameChars. Melakukan pencarian hari ini dan ada patokan yang cukup bagus di sini di StackOverflow yang menunjukkan hashset akan mengambil sedikit lebih dari setengah waktu array / daftar untuk 40 item: https://stackoverflow.com/a/10762995/949129

Inilah kelas pembantu yang saya gunakan untuk membersihkan jalur. Saya lupa sekarang mengapa saya memiliki opsi penggantian yang mewah di dalamnya, tetapi itu ada sebagai bonus lucu.

Metode bonus tambahan "IsValidLocalPath" juga :)

(** Yang tidak menggunakan ekspresi reguler)

public static class PathExtensions
{
    private static HashSet<char> _invalidFilenameChars;
    private static HashSet<char> InvalidFilenameChars
    {
        get { return _invalidFilenameChars ?? (_invalidFilenameChars = new HashSet<char>(Path.GetInvalidFileNameChars())); }
    }


    /// <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 remove bad characters.</param>
    /// <param name="fancyReplacements">TRUE 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, "_" is returned.</returns>
    public static string ToValidFilename(this string text, char? replacement = '_', bool fancyReplacements = false)
    {
        StringBuilder sb = new StringBuilder(text.Length);
        HashSet<char> invalids = InvalidFilenameChars;
        bool changed = false;

        for (int i = 0; i < text.Length; i++)
        {
            char c = text[i];
            if (invalids.Contains(c))
            {
                changed = true;
                char repl = replacement ?? '\0';
                if (fancyReplacements)
                {
                    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 != '\0')
                    sb.Append(repl);
            }
            else
                sb.Append(c);
        }

        if (sb.Length == 0)
            return "_";

        return changed ? sb.ToString() : text;
    }


    /// <summary>
    /// Returns TRUE if the specified path is a valid, local filesystem path.
    /// </summary>
    /// <param name="pathString"></param>
    /// <returns></returns>
    public static bool IsValidLocalPath(this string pathString)
    {
        // From solution at https://stackoverflow.com/a/11636052/949129
        Uri pathUri;
        Boolean isValidUri = Uri.TryCreate(pathString, UriKind.Absolute, out pathUri);
        return isValidUri && pathUri != null && pathUri.IsLoopback;
    }
}
Daniel Scott
sumber
2
public static class StringExtensions
      {
        public static string RemoveUnnecessary(this string source)
        {
            string result = string.Empty;
            string regex = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
            Regex reg = new Regex(string.Format("[{0}]", Regex.Escape(regex)));
            result = reg.Replace(source, "");
            return result;
        }
    }

Anda dapat menggunakan metode dengan jelas.

aemre
sumber
2

Nama file tidak boleh mengandung karakter dari Path.GetInvalidPathChars(), +dan #simbol, dan nama spesifik lainnya. Kami menggabungkan semua cek menjadi satu kelas:

public static class FileNameExtensions
{
    private static readonly Lazy<string[]> InvalidFileNameChars =
        new Lazy<string[]>(() => Path.GetInvalidPathChars()
            .Union(Path.GetInvalidFileNameChars()
            .Union(new[] { '+', '#' })).Select(c => c.ToString(CultureInfo.InvariantCulture)).ToArray());


    private static readonly HashSet<string> ProhibitedNames = new HashSet<string>
    {
        @"aux",
        @"con",
        @"clock$",
        @"nul",
        @"prn",

        @"com1",
        @"com2",
        @"com3",
        @"com4",
        @"com5",
        @"com6",
        @"com7",
        @"com8",
        @"com9",

        @"lpt1",
        @"lpt2",
        @"lpt3",
        @"lpt4",
        @"lpt5",
        @"lpt6",
        @"lpt7",
        @"lpt8",
        @"lpt9"
    };

    public static bool IsValidFileName(string fileName)
    {
        return !string.IsNullOrWhiteSpace(fileName)
            && fileName.All(o => !IsInvalidFileNameChar(o))
            && !IsProhibitedName(fileName);
    }

    public static bool IsProhibitedName(string fileName)
    {
        return ProhibitedNames.Contains(fileName.ToLower(CultureInfo.InvariantCulture));
    }

    private static string ReplaceInvalidFileNameSymbols([CanBeNull] this string value, string replacementValue)
    {
        if (value == null)
        {
            return null;
        }

        return InvalidFileNameChars.Value.Aggregate(new StringBuilder(value),
            (sb, currentChar) => sb.Replace(currentChar, replacementValue)).ToString();
    }

    public static bool IsInvalidFileNameChar(char value)
    {
        return InvalidFileNameChars.Value.Contains(value.ToString(CultureInfo.InvariantCulture));
    }

    public static string GetValidFileName([NotNull] this string value)
    {
        return GetValidFileName(value, @"_");
    }

    public static string GetValidFileName([NotNull] this string value, string replacementValue)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            throw new ArgumentException(@"value should be non empty", nameof(value));
        }

        if (IsProhibitedName(value))
        {
            return (string.IsNullOrWhiteSpace(replacementValue) ? @"_" : replacementValue) + value; 
        }

        return ReplaceInvalidFileNameSymbols(value, replacementValue);
    }

    public static string GetFileNameError(string fileName)
    {
        if (string.IsNullOrWhiteSpace(fileName))
        {
            return CommonResources.SelectReportNameError;
        }

        if (IsProhibitedName(fileName))
        {
            return CommonResources.FileNameIsProhibited;
        }

        var invalidChars = fileName.Where(IsInvalidFileNameChar).Distinct().ToArray();

        if(invalidChars.Length > 0)
        {
            return string.Format(CultureInfo.CurrentCulture,
                invalidChars.Length == 1 ? CommonResources.InvalidCharacter : CommonResources.InvalidCharacters,
                StringExtensions.JoinQuoted(@",", @"'", invalidChars.Select(c => c.ToString(CultureInfo.CurrentCulture))));
        }

        return string.Empty;
    }
}

Metode GetValidFileNamemenggantikan semua data yang salah _.

Punggung
sumber
2

Satu liner untuk membersihkan string dari karakter ilegal apa pun untuk penamaan file windows:

public static string CleanIllegalName(string p_testName) => new Regex(string.Format("[{0}]", Regex.Escape(new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars())))).Replace(p_testName, "");
Zananok
sumber
1
public static bool IsValidFilename(string testName)
{
    return !new Regex("[" + Regex.Escape(new String(System.IO.Path.GetInvalidFileNameChars())) + "]").IsMatch(testName);
}
mbdavis
sumber
0

Ini akan seperti yang Anda inginkan, dan menghindari tabrakan

 static string SanitiseFilename(string key)
    {
        var invalidChars = Path.GetInvalidFileNameChars();
        var sb = new StringBuilder();
        foreach (var c in key)
        {
            var invalidCharIndex = -1;
            for (var i = 0; i < invalidChars.Length; i++)
            {
                if (c == invalidChars[i])
                {
                    invalidCharIndex = i;
                }
            }
            if (invalidCharIndex > -1)
            {
                sb.Append("_").Append(invalidCharIndex);
                continue;
            }

            if (c == '_')
            {
                sb.Append("__");
                continue;
            }

            sb.Append(c);
        }
        return sb.ToString();

    }
mcintyre321
sumber
0

Saya pikir pertanyaan sudah tidak dijawab penuh ... Jawabannya hanya menggambarkan nama file bersih ATAU jalur ... tidak keduanya. Inilah solusi saya:

private static string CleanPath(string path)
{
    string regexSearch = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
    Regex r = new Regex(string.Format("[{0}]", Regex.Escape(regexSearch)));
    List<string> split = path.Split('\\').ToList();
    string returnValue = split.Aggregate(string.Empty, (current, s) => current + (r.Replace(s, "") + @"\"));
    returnValue = returnValue.TrimEnd('\\');
    return returnValue;
}
Suplanus
sumber
0

Saya membuat metode ekstensi yang menggabungkan beberapa saran:

  1. Memegang karakter ilegal dalam satu set hash
  2. Memfilter karakter di bawah ascii 127. Sejak Path.GetInvalidFileNameChars tidak menyertakan semua karakter yang tidak valid dengan kode ascii dari 0 hingga 255. Lihat di sini dan MSDN
  3. Kemungkinan untuk menentukan karakter pengganti

Sumber:

public static class FileNameCorrector
{
    private static HashSet<char> invalid = new HashSet<char>(Path.GetInvalidFileNameChars());

    public static string ToValidFileName(this string name, char replacement = '\0')
    {
        var builder = new StringBuilder();
        foreach (var cur in name)
        {
            if (cur > 31 && cur < 128 && !invalid.Contains(cur))
            {
                builder.Append(cur);
            }
            else if (replacement != '\0')
            {
                builder.Append(replacement);
            }
        }

        return builder.ToString();
    }
}
schoetbi
sumber
0

Berikut adalah fungsi yang menggantikan semua karakter ilegal dalam nama file dengan karakter pengganti:

public static string ReplaceIllegalFileChars(string FileNameWithoutPath, char ReplacementChar)
{
  const string IllegalFileChars = "*?/\\:<>|\"";
  StringBuilder sb = new StringBuilder(FileNameWithoutPath.Length);
  char c;

  for (int i = 0; i < FileNameWithoutPath.Length; i++)
  {
    c = FileNameWithoutPath[i];
    if (IllegalFileChars.IndexOf(c) >= 0)
    {
      c = ReplacementChar;
    }
    sb.Append(c);
  }
  return (sb.ToString());
}

Misalnya garis bawah dapat digunakan sebagai karakter pengganti:

NewFileName = ReplaceIllegalFileChars(FileName, '_');
Hans-Peter Kalb
sumber
Selain jawaban yang Anda berikan, silakan pertimbangkan untuk memberikan penjelasan singkat tentang mengapa dan bagaimana ini memperbaiki masalah.
jtate
-7

Atau Anda bisa melakukannya

[YOUR STRING].Replace('\\', ' ').Replace('/', ' ').Replace('"', ' ').Replace('*', ' ').Replace(':', ' ').Replace('?', ' ').Replace('<', ' ').Replace('>', ' ').Replace('|', ' ').Trim();
Danny Fallas
sumber