Saya melakukan hal yang sama hari ini. Saya tidak memeriksa SO untuk beberapa alasan, tetapi tetap menemukan jawabannya.
Aaron Smith
Jawaban:
154
Coba sesuatu seperti ini:
string fileName ="something";foreach(char c inSystem.IO.Path.GetInvalidFileNameChars()){
fileName = fileName.Replace(c,'_');}
Edit:
Karena GetInvalidFileNameChars()akan menghasilkan 10 atau 15 karakter, lebih baik menggunakan a StringBuilderdaripada string sederhana; versi aslinya akan membutuhkan waktu lebih lama dan menggunakan lebih banyak memori.
Anda bisa menggunakan StringBuilder jika Anda mau, tetapi jika namanya pendek dan saya rasa itu tidak sepadan. Anda juga bisa membuat metode Anda sendiri untuk membuat karakter [] dan mengganti semua karakter yang salah dalam satu iterasi. Selalu lebih baik untuk tetap sederhana kecuali tidak berhasil, Anda mungkin memiliki leher botol yang lebih buruk
Probabilitas untuk memiliki 2+ karakter tidak valid yang berbeda dalam string sangat kecil sehingga peduli dengan kinerja string.Replace () tidak ada gunanya.
Serge Wautier
1
Solusi hebat, selain menarik, resharper menyarankan versi Linq ini: fileName = System.IO.Path.GetInvalidFileNameChars (). Aggregate (fileName, (current, c) => current.Replace (c, '_')); Saya ingin tahu apakah ada kemungkinan peningkatan kinerja di sana. Saya menyimpan yang asli untuk tujuan keterbacaan karena kinerja bukanlah perhatian terbesar saya. Tetapi jika ada yang tertarik, mungkin layak untuk dijadikan tolok ukur
chrispepper1989
1
@Anda Tidak perlu. file.name.txt.pdfadalah pdf yang valid. Windows hanya membaca yang terakhir .untuk ekstensi.
Diego Jancic
33
fileName = fileName.Replace(":","-")
Namun ":" bukan satu-satunya karakter ilegal untuk Windows. Anda juga harus menangani:
/, \, :,*,?,", <, > and |
Ini terkandung dalam System.IO.Path.GetInvalidFileNameChars ();
Juga (di Windows), "." tidak bisa menjadi satu-satunya karakter dalam nama file (baik ".", "..", "...", dan seterusnya tidak valid). Berhati-hatilah saat menamai file dengan ".", Misalnya:
echo "test">.test.
Akan menghasilkan file bernama ".test"
Terakhir, jika Anda benar - benar ingin melakukan sesuatu dengan benar, ada beberapa nama file khusus yang perlu Anda perhatikan. Di Windows, Anda tidak dapat membuat file dengan nama:
Saya tidak pernah tahu tentang nama yang dicadangkan. Masuk akal meskipun
Greg Dean
4
Juga, untuk apa nilainya, Anda tidak dapat membuat nama file yang dimulai dengan salah satu nama yang dicadangkan ini, diikuti dengan desimal. yaitu con.air.avi
John Conrad
".foo" adalah nama file yang valid. Tidak tahu tentang nama file "CON" - untuk apa ini?
konfigurator
Gores itu. CON untuk konsol.
konfigurator
Terima kasih konfigurator; Saya telah memperbarui jawabannya, Anda benar ".foo" valid; namun ".foo." mengarah pada hasil yang mungkin dan tidak diinginkan. Diperbarui.
Phil Price
13
Ini tidak lebih efisien, tapi lebih menyenangkan :)
var fileName ="foo:bar";var invalidChars =System.IO.Path.GetInvalidFileNameChars();var cleanFileName =newstring(fileName.Where(m =>!invalidChars.Contains(m)).ToArray<char>());
Jika ada yang menginginkan versi yang dioptimalkan berdasarkan StringBuilder, gunakan ini. Termasuk trik rkagerer sebagai opsi.
staticchar[] _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>publicstaticstringMakeValidFileName(string text,char? replacement ='_',bool fancy =true){StringBuilder sb =newStringBuilder(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 ??'\0';if(fancy){if(c =='"') repl ='”';// U+201D right double quotation markelseif(c =='\'') repl ='’';// U+2019 right single quotation markelseif(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;}
1 untuk kode yang bagus dan mudah dibaca. Membuat sangat mudah untuk membaca & memperhatikan bug: P .. Fungsi ini harus selalu mengembalikan string asli karena diubah tidak akan pernah benar.
Erti-Chris Eelmaa
Terima kasih, saya pikir sudah lebih baik sekarang. Anda tahu apa yang mereka katakan tentang open source, "banyak mata membuat semua bug menjadi dangkal jadi saya tidak perlu menulis pengujian unit" ...
Qwertie
8
Berikut sedikit perubahan pada jawaban Diego.
Jika Anda tidak takut dengan Unicode, Anda dapat mempertahankan sedikit lebih banyak ketepatan dengan mengganti karakter yang tidak valid dengan simbol Unicode valid yang mirip dengan mereka. Inilah kode yang saya gunakan dalam proyek baru-baru ini yang melibatkan daftar potong kayu:
staticstringMakeValidFilename(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 slashforeach(char c inSystem.IO.Path.GetInvalidFileNameChars()){
text = text.Replace(c,'_');}return text;}
Ini menghasilkan nama file seperti 1⁄2” spruce.txtbukan1_2_ spruce.txt
Ya, ini benar-benar berfungsi:
Caveat Emptor
Saya tahu trik ini akan berfungsi pada NTFS tetapi saya terkejut menemukannya juga berfungsi pada partisi FAT dan FAT32. Itu karena nama file panjang yang disimpan dalam Unicode , bahkan sejauh sebagai Windows 95 / NT. Saya menguji pada Win7, XP, dan bahkan router berbasis Linux dan mereka muncul dengan baik. Tidak bisa mengatakan hal yang sama untuk di dalam DOSBox.
Karena itu, sebelum Anda menjadi gila dengan ini, pertimbangkan apakah Anda benar-benar membutuhkan kesetiaan ekstra. Mirip dengan Unicode dapat membingungkan orang atau program lama, misalnya OS lama yang mengandalkan halaman kode .
Diego memang memiliki solusi yang tepat, tetapi ada satu kesalahan kecil di sana. Versi string.Replace yang digunakan harus string.Replace (char, char), tidak ada string. Ganti (char, string)
Saya tidak dapat mengedit jawabannya atau saya akan membuat perubahan kecil.
Jadi seharusnya:
string fileName ="something";foreach(char c inSystem.IO.Path.GetInvalidFileNameChars()){
fileName = fileName.Replace(c,'_');}
Berikut adalah versi yang menggunakan StringBuilderdan IndexOfAnydengan penambahan massal untuk efisiensi penuh. Ini juga mengembalikan string asli daripada membuat string duplikat.
Last but not least, ia memiliki pernyataan switch yang mengembalikan karakter yang mirip yang dapat Anda sesuaikan sesuka Anda. Lihat pencarian membingungkan Unicode.org untuk melihat opsi apa yang mungkin Anda miliki, tergantung pada fontnya.
publicstaticstringGetSafeFilename(string arbitraryString){var invalidChars =System.IO.Path.GetInvalidFileNameChars();var replaceIndex = arbitraryString.IndexOfAny(invalidChars,0);if(replaceIndex ==-1)return arbitraryString;var r =newStringBuilder();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'\0':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();}
Ini tidak memeriksa ., ..atau nama-nama yang dicadangkan seperti CONkarena tidak jelas apa pengganti harus.
Saya membutuhkan sistem yang tidak bisa membuat tabrakan jadi saya tidak bisa memetakan banyak karakter menjadi satu. Saya berakhir dengan:
publicstaticclassExtension{/// <summary>/// Characters allowed in a file name. Note that curly braces don't show up here/// becausee they are used for escaping invalid characters./// </summary>privatestaticreadonlyHashSet<char>CleanFileNameChars=newHashSet<char>{' ','!','#','$','%','&','\'','(',')','+',',','-','.','0','1','2','3','4','5','6','7','8','9','=','@','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','[',']','^','_','`','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',};/// <summary>/// Creates a clean file name from one that may contain invalid characters in /// a way that will not collide./// </summary>/// <param name="dirtyFileName">/// The file name that may contain invalid filename characters./// </param>/// <returns>/// A file name that does not contain invalid filename characters./// </returns>/// <remarks>/// <para>/// Escapes invalid characters by converting their ASCII values to hexadecimal/// and wrapping that value in curly braces. Curly braces are escaped by doubling/// them, for example '{' => "{{"./// </para>/// <para>/// Note that although NTFS allows unicode characters in file names, this/// method does not./// </para>/// </remarks>publicstaticstringCleanFileName(thisstring dirtyFileName){stringEscapeHexString(char c)=>"{"+(c >255? $"{(uint)c:X4}": $"{(uint)c:X2}")+"}";returnstring.Join(string.Empty,
dirtyFileName.Select(
c =>
c =='{'?"{{":
c =='}'?"}}":CleanFileNameChars.Contains(c)? $"{c}":EscapeHexString(c)));}}
Saya perlu melakukan ini hari ini ... dalam kasus saya, saya perlu menggabungkan nama pelanggan dengan tanggal dan waktu untuk file .kmz akhir. Solusi terakhir saya adalah ini:
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));
Anda bahkan dapat membuatnya mengganti spasi jika Anda menambahkan spasi char ke array yang tidak valid.
Mungkin ini bukan yang tercepat, tetapi karena kinerja bukanlah masalah, saya menganggapnya elegan dan dapat dimengerti.
Jawaban:
Coba sesuatu seperti ini:
Edit:
Karena
GetInvalidFileNameChars()
akan menghasilkan 10 atau 15 karakter, lebih baik menggunakan aStringBuilder
daripada string sederhana; versi aslinya akan membutuhkan waktu lebih lama dan menggunakan lebih banyak memori.sumber
file.name.txt.pdf
adalah pdf yang valid. Windows hanya membaca yang terakhir.
untuk ekstensi.Namun ":" bukan satu-satunya karakter ilegal untuk Windows. Anda juga harus menangani:
Ini terkandung dalam System.IO.Path.GetInvalidFileNameChars ();
Juga (di Windows), "." tidak bisa menjadi satu-satunya karakter dalam nama file (baik ".", "..", "...", dan seterusnya tidak valid). Berhati-hatilah saat menamai file dengan ".", Misalnya:
Akan menghasilkan file bernama ".test"
Terakhir, jika Anda benar - benar ingin melakukan sesuatu dengan benar, ada beberapa nama file khusus yang perlu Anda perhatikan. Di Windows, Anda tidak dapat membuat file dengan nama:
sumber
Ini tidak lebih efisien, tapi lebih menyenangkan :)
sumber
Jika ada yang menginginkan versi yang dioptimalkan berdasarkan
StringBuilder
, gunakan ini. Termasuk trik rkagerer sebagai opsi.sumber
Berikut sedikit perubahan pada jawaban Diego.
Jika Anda tidak takut dengan Unicode, Anda dapat mempertahankan sedikit lebih banyak ketepatan dengan mengganti karakter yang tidak valid dengan simbol Unicode valid yang mirip dengan mereka. Inilah kode yang saya gunakan dalam proyek baru-baru ini yang melibatkan daftar potong kayu:
Ini menghasilkan nama file seperti
1⁄2” spruce.txt
bukan1_2_ spruce.txt
Ya, ini benar-benar berfungsi:
Caveat Emptor
Saya tahu trik ini akan berfungsi pada NTFS tetapi saya terkejut menemukannya juga berfungsi pada partisi FAT dan FAT32. Itu karena nama file panjang yang disimpan dalam Unicode , bahkan sejauh sebagai Windows 95 / NT. Saya menguji pada Win7, XP, dan bahkan router berbasis Linux dan mereka muncul dengan baik. Tidak bisa mengatakan hal yang sama untuk di dalam DOSBox.
Karena itu, sebelum Anda menjadi gila dengan ini, pertimbangkan apakah Anda benar-benar membutuhkan kesetiaan ekstra. Mirip dengan Unicode dapat membingungkan orang atau program lama, misalnya OS lama yang mengandalkan halaman kode .
sumber
Berikut adalah versi jawaban yang diterima
Linq
yang menggunakanEnumerable.Aggregate
:sumber
Diego memang memiliki solusi yang tepat, tetapi ada satu kesalahan kecil di sana. Versi string.Replace yang digunakan harus string.Replace (char, char), tidak ada string. Ganti (char, string)
Saya tidak dapat mengedit jawabannya atau saya akan membuat perubahan kecil.
Jadi seharusnya:
sumber
Berikut adalah versi yang menggunakan
StringBuilder
danIndexOfAny
dengan penambahan massal untuk efisiensi penuh. Ini juga mengembalikan string asli daripada membuat string duplikat.Last but not least, ia memiliki pernyataan switch yang mengembalikan karakter yang mirip yang dapat Anda sesuaikan sesuka Anda. Lihat pencarian membingungkan Unicode.org untuk melihat opsi apa yang mungkin Anda miliki, tergantung pada fontnya.
Ini tidak memeriksa
.
,..
atau nama-nama yang dicadangkan sepertiCON
karena tidak jelas apa pengganti harus.sumber
Membersihkan sedikit kode saya dan membuat sedikit refactoring ... Saya membuat ekstensi untuk tipe string:
Sekarang lebih mudah digunakan dengan:
Jika Anda ingin mengganti dengan karakter yang berbeda dari "_", Anda dapat menggunakan:
Dan Anda dapat menambahkan karakter untuk diganti .. misalnya Anda tidak ingin spasi atau koma:
Semoga membantu ...
Bersulang
sumber
Solusi sederhana lainnya:
sumber
Kode satu baris sederhana:
Anda dapat membungkusnya dengan metode ekstensi jika Anda ingin menggunakannya kembali.
sumber
Saya membutuhkan sistem yang tidak bisa membuat tabrakan jadi saya tidak bisa memetakan banyak karakter menjadi satu. Saya berakhir dengan:
sumber
Saya perlu melakukan ini hari ini ... dalam kasus saya, saya perlu menggabungkan nama pelanggan dengan tanggal dan waktu untuk file .kmz akhir. Solusi terakhir saya adalah ini:
Anda bahkan dapat membuatnya mengganti spasi jika Anda menambahkan spasi char ke array yang tidak valid.
Mungkin ini bukan yang tercepat, tetapi karena kinerja bukanlah masalah, saya menganggapnya elegan dan dapat dimengerti.
Bersulang!
sumber
Anda dapat melakukan ini dengan
sed
perintah:sumber