2008-12-02 26 views
71

Chương trình của tôi sẽ lấy các chuỗi tùy ý từ internet và sử dụng chúng cho tên tệp. Có một cách đơn giản để loại bỏ các ký tự xấu từ các chuỗi này hay tôi cần phải viết một hàm tùy chỉnh cho điều này?Có cách nào để tạo chuỗi đường dẫn an toàn trong C#?

+0

bản sao có thể có của [Trình dọn dẹp tên tệp an toàn/được phép cho .NET] (http://stackoverflow.com/questions/1862993/safe-allowed-filename-cleaner-for-net) – N8allan

Trả lời

148

Ugh, tôi ghét nó khi mọi người cố gắng đoán xem ký tự nào hợp lệ. Bên cạnh việc hoàn toàn không di chuyển (luôn nghĩ về Mono), cả hai nhận xét trước đó đều bỏ lỡ hơn 25 ký tự không hợp lệ.

'Clean just a filename 
Dim filename As String = "salmnas dlajhdla kjha;dmas'lkasn" 
For Each c In IO.Path.GetInvalidFileNameChars 
    filename = filename.Replace(c, "") 
Next 

'See also IO.Path.GetInvalidPathChars 
+0

Nó sẽ không tạo ra nhiều khác biệt trong tình huống này. Lỗi Windows chỉ phàn nàn về số lượng ký tự đó. Cảm ơn bạn đã chỉ ra các GetInvalidFileNameChars, mặc dù, tôi sẽ không đi qua mà trước. Tôi sẽ ghi nhớ điều đó. – BenAlabaster

+65

Phiên bản C#: foreach (var c trong Path.GetInvalidFileNameChars()) {fileName = fileName.Replace (c, '-'); } – jcollum

+8

Giải pháp này xử lý xung đột tên như thế nào? Có vẻ như nhiều hơn một chuỗi có thể khớp với một tên tệp duy nhất (ví dụ: "Địa ngục?" Và "Địa ngục *"). Nếu bạn ổn, chỉ xóa các ký tự vi phạm sau đó tốt; nếu không bạn cần phải cẩn thận để xử lý xung đột tên. –

19

Tôi đồng ý với Grauenwolf và rất muốn giới thiệu các Path.GetInvalidFileNameChars()

Dưới đây là tôi C# đóng góp:

string file = @"38?/.\}[+=n a882 a.a*/|n^%$ ad#(-))"; 
Array.ForEach(Path.GetInvalidFileNameChars(), 
     c => file = file.Replace(c.ToString(), String.Empty)); 

tái bút: - điều này khó hiểu hơn là - tôi đã cố gắng súc tích.

+3

Tại sao trên thế giới bạn sử dụng 'Array.ForEach' thay vì chỉ' foreach' ở đây –

+8

Nếu bạn muốn thậm chí còn ngắn gọn hơn/khó hiểu hơn: 'Path.GetInvalidFileNameChars(). Tổng hợp (tệp, (current, c) => current.Replace (c, '-')) ' –

+0

@ BlueRaja-DannyPflughoeft Vì bạn muốn làm chậm hơn? –

6

Nếu bạn muốn nhanh chóng loại bỏ tất cả các ký tự đặc biệt đó là người sử dụng đôi khi dễ đọc hơn cho tên tập tin này làm việc độc đáo:

string myCrazyName = "q`w^[email protected]#y$u%i^o&p*a(s)d_f-g+h=j{k}l|z:x\"c<v>b?n[m]q\\w;e'r,t.y/u"; 
string safeName = Regex.Replace(
    myCrazyName, 
    "\W", /*Matches any nonword character. Equivalent to '[^A-Za-z0-9_]'*/ 
    "", 
    RegexOptions.IgnoreCase); 
// safeName == "qwertyuiopasd_fghjklzxcvbnmqwertyu" 
+1

thực sự '\ W' khớp với nhiều hơn không phải alpha-numerics (' [^ A-Za-z0-9_] '). Tất cả các ký tự 'từ' Unicode (русский 中文 ..., v.v.) cũng sẽ không được thay thế. Nhưng đây là một điều tốt. – Ishmael

+0

Nhược điểm duy nhất là điều này cũng loại bỏ '.' vì vậy bạn phải giải nén phần mở rộng đầu tiên, và thêm nó lại sau. – awe

10

Đây là chức năng mà tôi đang sử dụng hiện nay (nhờ jcollum cho C# chẳng hạn) :

public static string MakeSafeFilename(string filename, char replaceChar) 
{ 
    foreach (char c in System.IO.Path.GetInvalidFileNameChars()) 
    { 
     filename = filename.Replace(c, replaceChar); 
    } 
    return filename; 
} 

Tôi chỉ đặt điều này trong lớp "Người trợ giúp" để thuận tiện.

20

Tước ký tự không hợp lệ:

static readonly char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); 

// Builds a string out of valid chars 
var validFilename = new string(filename.Where(ch => !invalidFileNameChars.Contains(ch)).ToArray()); 

Để thay thế ký tự không hợp lệ:

static readonly char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); 

// Builds a string out of valid chars and an _ for invalid ones 
var validFilename = new string(filename.Select(ch => invalidFileNameChars.Contains(ch) ? '_' : ch).ToArray()); 

Để thay thế ký tự không hợp lệ (và tránh tiềm năng tên xung đột như Hell * vs Hell $):

static readonly IList<char> invalidFileNameChars = Path.GetInvalidFileNameChars(); 

// Builds a string out of valid chars and replaces invalid chars with a unique letter (Moves the Char into the letter range of unicode, starting at "A") 
var validFilename = new string(filename.Select(ch => invalidFileNameChars.Contains(ch) ? Convert.ToChar(invalidFileNameChars.IndexOf(ch) + 65) : ch).ToArray()); 
+0

"dải ký tự không hợp lệ" hoạt động như một nét duyên dáng. cảm ơn. – Nani

29

Câu hỏi này đã được yêu cầu manytimesbefore và, như được chỉ ra nhiều lần trước đây, IO.Path.GetInvalidFileNameChars không đủ.

Đầu tiên, có nhiều tên như PRN và CON được đặt trước và không được phép cho tên tệp. Có những tên khác không được phép chỉ ở thư mục gốc. Các tên kết thúc trong một khoảng thời gian cũng không được phép.

Thứ hai, có nhiều giới hạn độ dài khác nhau. Đọc danh sách đầy đủ cho NTFS here.

Thứ ba, bạn có thể đính kèm vào hệ thống tệp có các giới hạn khác. Ví dụ: tên tệp ISO 9660 không thể bắt đầu bằng "-" nhưng có thể chứa nó.

Thứ tư, bạn sẽ làm gì nếu hai quá trình "tùy tiện" chọn cùng một tên?

Nói chung, việc sử dụng tên được tạo bên ngoài cho tên tệp là một ý tưởng tồi. Tôi đề nghị tạo tên tệp riêng của bạn và lưu trữ tên người có thể đọc được trong nội bộ.

+11

Mặc dù bạn là chính xác về mặt kỹ thuật, GetInvalidFileNameChars là tốt cho 80% + của các tình huống bạn muốn sử dụng nó trong, do đó nó là một câu trả lời tốt. Câu trả lời của bạn sẽ thích hợp hơn khi nhận xét về câu trả lời được chấp nhận mà tôi nghĩ. – CubanX

+4

Tôi đồng ý với DourHighArch. Lưu tệp nội bộ dưới dạng guid, tham chiếu đến "tên thân thiện" được lưu trữ trong cơ sở dữ liệu. Đừng để người dùng kiểm soát đường dẫn của bạn trên trang web hoặc họ sẽ cố gắng đánh cắp web.config của bạn. Nếu bạn kết hợp viết lại url để làm cho nó sạch sẽ nó sẽ chỉ làm việc cho các url thân thiện phù hợp trong cơ sở dữ liệu. – rtpHarry

1

tôi thấy sử dụng này là nhanh chóng và dễ hiểu:

<Extension()> 
Public Function MakeSafeFileName(FileName As String) As String 
    Return FileName.Where(Function(x) Not IO.Path.GetInvalidFileNameChars.Contains(x)).ToArray 
End Function 

này hoạt động vì một stringIEnumerable như một mảng char và có một chuỗi string constructor mà phải mất một mảng char.

4
static class Utils 
{ 
    public static string MakeFileSystemSafe(this string s) 
    { 
     return new string(s.Where(IsFileSystemSafe).ToArray()); 
    } 

    public static bool IsFileSystemSafe(char c) 
    { 
     return !Path.GetInvalidFileNameChars().Contains(c); 
    } 
} 
4

Đây là những gì tôi chỉ thêm vào ClipFlair của (http://clipflair.codeplex.com) StringExtensions lớp tĩnh (dự án Utils.Silverlight), dựa trên thông tin thu thập được từ các liên kết đến câu hỏi stackoverflow liên quan đăng bởi Dour cao Arch trên:

public static string ReplaceInvalidFileNameChars(this string s, string replacement = "") 
{ 
    return Regex.Replace(s, 
    "[" + Regex.Escape(new String(System.IO.Path.GetInvalidPathChars())) + "]", 
    replacement, //can even use a replacement string of any length 
    RegexOptions.IgnoreCase); 
    //not using System.IO.Path.InvalidPathChars (deprecated insecure API) 
} 
11

Dưới đây là phiên bản của tôi:

static string GetSafeFileName(string name, char replace = '_') { 
    char[] invalids = Path.GetInvalidFileNameChars(); 
    return new string(name.Select(c => invalids.Contains(c) ? replace : c).ToArray()); 
} 

tôi không chắc chắn như thế nào là kết quả của GetInvalidFileNameChars được tính, nhưng "Nhận" cho thấy nó không đố l, vì vậy tôi lưu trữ kết quả. Hơn nữa, điều này chỉ đi qua chuỗi đầu vào một lần thay vì nhiều lần, như các giải pháp ở trên mà lặp qua tập hợp các ký tự không hợp lệ, thay thế chúng trong chuỗi nguồn một tại một thời điểm. Ngoài ra, tôi thích các giải pháp ở đâu đó, nhưng tôi thích thay thế các ký tự không hợp lệ thay vì xóa chúng. Cuối cùng, thay thế của tôi chính xác là một ký tự để tránh chuyển đổi ký tự thành chuỗi khi tôi lặp qua chuỗi.

Tôi nói tất cả những điều đó đang làm hồ sơ - điều này chỉ "cảm thấy" tốt với tôi. :)

+1

Bạn có thể làm 'mới HashSet (Path.GetInvalidFileNameChars())' để tránh O (n) liệt kê - vi tối ưu hóa. – TrueWill

2
private void textBoxFileName_KeyPress(object sender, KeyPressEventArgs e) 
{ 
    e.Handled = CheckFileNameSafeCharacters(e); 
} 

/// <summary> 
/// This is a good function for making sure that a user who is naming a file uses proper characters 
/// </summary> 
/// <param name="e"></param> 
/// <returns></returns> 
internal static bool CheckFileNameSafeCharacters(System.Windows.Forms.KeyPressEventArgs e) 
{ 
    if (e.KeyChar.Equals(24) || 
     e.KeyChar.Equals(3) || 
     e.KeyChar.Equals(22) || 
     e.KeyChar.Equals(26) || 
     e.KeyChar.Equals(25))//Control-X, C, V, Z and Y 
      return false; 
    if (e.KeyChar.Equals('\b'))//backspace 
     return false; 

    char[] charArray = Path.GetInvalidFileNameChars(); 
    if (charArray.Contains(e.KeyChar)) 
     return true;//Stop the character from being entered into the control since it is non-numerical 
    else 
     return false;    
} 
2

Tại sao không chuyển đổi chuỗi thành một tương đương Base64 như thế này:

string UnsafeFileName = "salmnas dlajhdla kjha;dmas'lkasn"; 
string SafeFileName = Convert.ToBase64String(Encoding.UTF8.GetBytes(UnsafeFileName)); 

Nếu bạn muốn chuyển đổi nó trở lại, do đó bạn có thể đọc nó:

UnsafeFileName = Encoding.UTF8.GetString(Convert.FromBase64String(SafeFileName)); 

tôi đã sử dụng điều này để lưu tệp PNG với tên duy nhất từ ​​mô tả ngẫu nhiên.

Các vấn đề liên quan