Làm thế nào để loại bỏ các ký tự bất hợp pháp khỏi đường dẫn và tên tệp?


456

Tôi cần một cách mạnh mẽ và đơn giản để loại bỏ các ký tự và đường dẫn tệp bất hợp pháp khỏi một chuỗi đơn giản. Tôi đã sử dụng đoạn mã dưới đây nhưng dường như nó không làm gì cả, tôi còn thiếu gì?

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

1
Trim loại bỏ các ký tự từ đầu và cuối chuỗi. Tuy nhiên, có lẽ bạn nên hỏi tại sao dữ liệu không hợp lệ, thay vì thử và vệ sinh / sửa chữa dữ liệu, từ chối dữ liệu.
dùng7116

8
Tên kiểu Unix không hợp lệ trên Windows và tôi không muốn xử lý 8.3 tên ngắn.
Gary Willoughby

GetInvalidFileNameChars()sẽ loại bỏ những thứ như: \ etc từ đường dẫn thư mục.
CAD bloke

1
Path.GetInvalidPathChars()dường như không thoát y *hoặc?
CAD bloke

19
Tôi đã thử nghiệm năm câu trả lời từ câu hỏi này (vòng lặp thời gian 100.000) và phương pháp sau là nhanh nhất. Biểu thức chính quy chiếm vị trí thứ 2 và chậm hơn 25%: chuỗi công khai GetSafeFilename (tên tệp chuỗi) {return string.Join ("_", filename.Split (Path.GetInvalidFileNameChars ())); }
Brain2000

Câu trả lời:


494

Thay vào đó hãy thử một cái gì đó như thế này;

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

Nhưng tôi phải đồng ý với các ý kiến, có lẽ tôi sẽ cố gắng xử lý nguồn gốc của các con đường bất hợp pháp, thay vì cố gắng đưa một con đường bất hợp pháp thành một con đường hợp pháp nhưng có thể ngoài ý muốn.

Chỉnh sửa: Hoặc một giải pháp có khả năng 'tốt hơn', sử dụng 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, "");

Tuy nhiên, câu hỏi xin được hỏi, tại sao bạn lại làm điều này ngay từ đầu.


40
Không cần thiết phải nối hai danh sách lại với nhau. Danh sách char tên tệp bất hợp pháp chứa danh sách char đường dẫn bất hợp pháp và có thêm một vài danh sách. Dưới đây là danh sách của cả hai danh sách được chuyển thành 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 điều này có thể đúng trên Windows và việc triển khai .NET của Microsoft Tôi không sẵn sàng đưa ra giả định tương tự khi nói Linux chạy đơn.
Matthew Scharley

7
Về giải pháp đầu tiên. StringBuilder không nên hiệu quả hơn các bài tập chuỗi?
epignosisx

6
Đối với giá trị của nó, @MatthewScharley, việc triển khai Mono của GetInvalidPathChars () chỉ trả về 0x00 và GetInvalidFileNameChars () chỉ trả về 0x00 và '/' khi chạy trên nền tảng không phải Windows. Trên Windows, danh sách các ký tự không hợp lệ dài hơn nhiều và GetInvalidPathChars () hoàn toàn trùng lặp bên trong GetInvalidFileNameChars (). Điều này sẽ không thay đổi trong tương lai có thể thấy được, vì vậy tất cả những gì bạn thực sự làm là tăng gấp đôi lượng thời gian mà chức năng này sẽ chạy vì bạn lo lắng rằng định nghĩa về một đường dẫn hợp lệ sẽ sớm thay đổi. Mà nó sẽ không.
Warren Rumak

13
@Charleh cuộc thảo luận này là không cần thiết ... mã phải luôn được tối ưu hóa và không có nguy cơ điều này là không chính xác. Tên tệp cũng là một phần của đường dẫn. Vì vậy, nó chỉ là phi logic GetInvalidPathChars()có thể chứa các nhân vật GetInvalidFileNameChars()sẽ không. Bạn không thực hiện đúng về tối ưu hóa "sớm". Bạn chỉ đơn giản là sử dụng mã xấu.
Stefan Fabian

354

Câu hỏi ban đầu được yêu cầu "loại bỏ các ký tự bất hợp pháp":

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

Thay vào đó, bạn có thể muốn thay thế chúng:

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

Câu trả lời này là về một chủ đề khác của Ceres , tôi thực sự thích nó gọn gàng và đơn giản.


10
Để trả lời chính xác câu hỏi của OP, bạn sẽ cần sử dụng "" thay vì "_", nhưng câu trả lời của bạn có thể áp dụng cho nhiều người trong chúng ta trong thực tế. Tôi nghĩ rằng việc thay thế các nhân vật bất hợp pháp bằng một số hợp pháp được thực hiện phổ biến hơn.
BH

37
Tôi đã thử nghiệm năm phương pháp từ câu hỏi này (vòng lặp thời gian 100.000) và phương pháp này là phương pháp nhanh nhất. Biểu thức chính quy chiếm vị trí thứ 2 và chậm hơn 25% so với phương pháp này.
Brain2000

10
Để giải quyết nhận xét của @BH, người ta chỉ cần sử dụng chuỗi.Concat (name.Split (Path.GetInvalidFileNameChars ()))
Michael Sutton

210

Tôi sử dụng Linq để dọn sạch tên tập tin. Bạn có thể dễ dàng mở rộng điều này để kiểm tra các đường dẫn hợp lệ là tốt.

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

Cập nhật

Một số ý kiến ​​cho thấy phương pháp này không hiệu quả với họ vì vậy tôi đã bao gồm một liên kết đến đoạn trích DotNetFiddle để bạn có thể xác thực phương thức.

https://dotnetfiddle.net/nw1SWY


4
Điều này đã không làm việc cho tôi. Phương thức này không trả về chuỗi sạch. Nó đang trả lại tên tệp đã qua.
Karan

Những gì @Karan nói, điều này không hoạt động, chuỗi ban đầu trở lại.
Jon

Bạn thực sự có thể làm điều này với Linq như thế này : var invalid = new HashSet<char>(Path.GetInvalidPathChars()); return new string(originalString.Where(s => !invalid.Contains(s)).ToArray()). Hiệu suất có thể không tốt nhưng điều đó có thể không quan trọng.
Casey

2
@Karan hoặc Jon Đầu vào nào bạn đang gửi chức năng này? Xem chỉnh sửa của tôi để xác minh phương pháp này.
Michael Minton

3
Thật dễ dàng - mọi người đã vượt qua các chuỗi với ký tự hợp lệ. Nâng cao cho giải pháp tổng hợp mát mẻ.
Nickmaovich

89

Bạn có thể xóa ký tự bất hợp pháp bằng Linq như thế này:

var invalidChars = Path.GetInvalidFileNameChars();

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

EDIT
Đây là giao diện của bản chỉnh sửa được yêu cầu trong các bình luận:

var invalidChars = Path.GetInvalidFileNameChars();

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

1
Tôi thích cách này: bạn chỉ giữ các ký tự được phép trong chuỗi (không có gì khác ngoài một mảng char).
Anh chàng Pascalou

6
Tôi biết rằng đây là một câu hỏi cũ, nhưng đây là một câu trả lời tuyệt vời. Tuy nhiên, tôi muốn thêm rằng trong c # bạn không thể chuyển từ char [] sang chuỗi ngầm hoặc rõ ràng (crazy, tôi biết) vì vậy bạn sẽ cần thả nó vào một hàm tạo chuỗi.
JNYRanger

1
Tôi chưa xác nhận điều này, nhưng tôi hy vọng Path.GetInvalidPathChars () sẽ là siêu bộ của GetInvalidFileNameChars () và để bao quát cả tên tệp và đường dẫn, vì vậy tôi có thể sẽ sử dụng nó để thay thế.
angensesen

3
@anjdreas thực sự là Path.GetInvalidPathChars () dường như là một tập hợp con của Path.GetInvalidFileNameChars (), không phải là cách khác. Ví dụ như Path.GetInvalidPathChars () sẽ không trả về '?'.
Rafael Costa

1
Đây là một câu trả lời tốt. Tôi sử dụng cả danh sách tên tệp và danh sách filepath: ____________________________ string cleanData = new string (data.Where (x =>! Path.GetInvalidFileNameChars (). Chứa (x) &&! Path.GetInvalidPathChars (). ToArray ());
goamn

27

Đây đều là những giải pháp tuyệt vời, nhưng tất cả đều dựa vào Path.GetInvalidFileNameChars, có thể không đáng tin cậy như bạn nghĩ. Lưu ý các lưu ý sau trong tài liệu MSDN về Path.GetInvalidFileNameChars:

Mảng được trả về từ phương thức này không được đảm bảo chứa toàn bộ các ký tự không hợp lệ trong tên tệp và thư mục. Toàn bộ các ký tự không hợp lệ có thể thay đổi theo hệ thống tệp. Ví dụ: trên các nền tảng máy tính để bàn dựa trên Windows, các ký tự đường dẫn không hợp lệ có thể bao gồm các ký tự ASCII / Unicode từ 1 đến 31, cũng như trích dẫn ("), nhỏ hơn (<), lớn hơn (>), pipe (|), backspace ( \ b), null (\ 0) và tab (\ t).

Nó không tốt hơn với Path.GetInvalidPathCharsphương pháp. Nó chứa chính xác nhận xét tương tự.


13
Vậy thì điểm của Path.GetInvalidFileNameChars là gì? Tôi hy vọng nó sẽ trả về chính xác các ký tự không hợp lệ cho hệ thống hiện tại, dựa vào .NET để biết hệ thống tập tin nào tôi đang chạy và trình bày cho tôi các ký tự không hợp lệ phù hợp. Nếu đây không phải là trường hợp và nó chỉ trả về các ký tự được mã hóa cứng, không đáng tin cậy ở vị trí đầu tiên, phương thức này nên được loại bỏ vì nó có giá trị bằng không.
Jan

1
Tôi biết đây là một nhận xét cũ, nhưng @Jan bạn có thể muốn viết trên một hệ thống tập tin khác, có thể đây là lý do tại sao có một cảnh báo.
fantastik78

3
@ fantastik78 điểm tốt, nhưng trong trường hợp này tôi muốn có thêm một đối số enum để chỉ định FS từ xa của mình. Nếu đây là quá nhiều nỗ lực bảo trì (rất có thể là trường hợp), toàn bộ phương pháp này vẫn là một ý tưởng tồi, bởi vì nó mang lại cho bạn ấn tượng sai về sự an toàn.
1 tháng

1
@Jan Tôi hoàn toàn đồng ý với bạn, tôi chỉ tranh luận về cảnh báo.
fantastik78

Điều thú vị là đây là một loại ký tự không hợp lệ "danh sách đen". Nó sẽ không tốt hơn để "danh sách trắng" chỉ các ký tự hợp lệ đã biết ở đây?! Nhắc nhở tôi về ý tưởng "virusscanner" ngu ngốc thay vì danh sách trắng cho phép các ứng dụng ....
Bernhard

26

Đối với tên tệp:

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

Đối với đường dẫn đầy đủ:

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

Lưu ý rằng nếu bạn có ý định sử dụng tính năng này như một tính năng bảo mật, một cách tiếp cận mạnh mẽ hơn sẽ là mở rộng tất cả các đường dẫn và sau đó xác minh rằng đường dẫn do người dùng cung cấp thực sự là con của một thư mục mà người dùng nên truy cập.


18

Đối với người mới bắt đầu, Trim chỉ xóa các ký tự từ đầu hoặc cuối chuỗi . Thứ hai, bạn nên đánh giá xem bạn có thực sự muốn xóa các ký tự gây khó chịu hay không nhanh và cho người dùng biết tên tệp của họ không hợp lệ. Lựa chọn của tôi là lựa chọn sau, nhưng ít nhất câu trả lời của tôi sẽ chỉ cho bạn cách làm mọi thứ đúng và sai:

Câu hỏi StackOverflow chỉ ra cách kiểm tra xem một chuỗi đã cho có phải là tên tệp hợp lệ hay không . Lưu ý bạn có thể sử dụng regex từ câu hỏi này để xóa các ký tự có thay thế biểu thức thông thường (nếu bạn thực sự cần phải làm điều này).


Tôi đặc biệt đồng ý với lời khuyên thứ hai.
OregonGhost

4
Tôi thường đồng ý với cái thứ hai, nhưng tôi có một chương trình tạo tên tệp và có thể chứa các ký tự không hợp lệ trong một số trường hợp. Vì chương trình của tôi đang tạo tên tệp bất hợp pháp, tôi nghĩ rằng việc loại bỏ / thay thế các ký tự đó là phù hợp. (Chỉ cần chỉ ra một trường hợp sử dụng hợp lệ)
JDB vẫn còn nhớ Monica

16

Cách tốt nhất để loại bỏ ký tự bất hợp pháp khỏi đầu vào của người dùng là thay thế ký tự bất hợp pháp bằng lớp Regex, tạo phương thức trong mã phía sau hoặc cũng xác thực ở phía máy khách bằng cách sử dụng điều khiển Thường xuyên.

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

HOẶC LÀ

<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">

5
IMHO giải pháp này tốt hơn nhiều so với các giải pháp khác Thay vì tìm kiếm tất cả các ký tự không hợp lệ chỉ cần xác định giá trị nào là hợp lệ.
igorushi

15

Tôi sử dụng các biểu thức thông thường để đạt được điều này. Đầu tiên, tôi tự động xây dựng regex.

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

Sau đó, tôi chỉ cần gọi removeInvalidChars.Replace để thực hiện tìm và thay thế. Điều này rõ ràng có thể được mở rộng để bao gồm cả ký tự đường dẫn.


Lạ thật, nó đã làm việc cho tôi. Tôi sẽ kiểm tra kỹ khi tôi có cơ hội. Bạn có thể cụ thể hơn và giải thích chính xác những gì không làm việc cho bạn?
Jeff Yates

1
Nó sẽ không hoạt động (ít nhất là đúng) vì bạn không thoát khỏi các ký tự đường dẫn đúng cách và một số trong số chúng có ý nghĩa đặc biệt. Tham khảo câu trả lời của tôi để biết cách làm điều đó.
Matthew Scharley

@Jeff: Phiên bản của bạn vẫn tốt hơn Matthew, nếu bạn sửa đổi một chút. Tham khảo câu trả lời của tôi về cách.
1

2
Tôi cũng sẽ thêm một số mẫu tên tệp không hợp lệ khác có thể tìm thấy trên MSDN và mở rộng giải pháp của bạn sang regex sau: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

Tôi hoàn toàn thích ý tưởng của Jeff Yates. Nó sẽ hoạt động hoàn hảo, nếu bạn sửa đổi một chút:

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

Sự cải thiện chỉ là để thoát khỏi regex được tạo tự động.


11

Đây là một đoạn mã sẽ giúp cho .NET 3 trở lên.

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

8

Hầu hết các giải pháp ở trên kết hợp các ký tự bất hợp pháp cho cả đường dẫn và tên tệp là sai (ngay cả khi cả hai cuộc gọi hiện trả về cùng một bộ ký tự). Trước tiên tôi sẽ phân chia đường dẫn + tên tệp trong đường dẫn và tên tệp, sau đó áp dụng tập hợp thích hợp cho cả hai và sau đó kết hợp cả hai lại.

wvd_vegt


+1: Rất đúng. Ngày nay, làm việc trong .NET 4.0, giải pháp regex từ câu trả lời hàng đầu đã loại bỏ tất cả các dấu gạch chéo ngược trong một đường dẫn đầy đủ. Vì vậy, tôi đã tạo một regex cho đường dẫn dir và một regex chỉ cho tên tệp, được làm sạch riêng và kết hợp lại
dario_ramos

Điều đó có thể đúng nhưng điều này không trả lời câu hỏi. Tôi không chắc chắn một điều mơ hồ 'Tôi sẽ làm như thế này' rất hữu ích so với một số giải pháp hoàn chỉnh đã có ở đây (xem ví dụ câu trả lời của Lilly, bên dưới)
Ian Grainger

6

Nếu bạn xóa hoặc thay thế bằng một ký tự các ký tự không hợp lệ, bạn có thể có các va chạm:

<abc -> abc
>abc -> abc

Đây là một phương pháp đơn giản để tránh điều này:

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

Kết quả:

 <abc -> [1]abc
 >abc -> [2]abc

5

Ném một ngoại lệ.

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

4

Tôi đã viết con quái vật này cho vui, nó cho phép bạn làm tròn:

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

1
Tôi thích điều này bởi vì nó tránh có hai chuỗi khác nhau tạo ra cùng một đường dẫn kết quả.
Kim

3

Tôi nghĩ việc xác nhận bằng cách sử dụng biểu thức chính quy và xác định những ký tự nào được phép dễ dàng hơn nhiều, thay vì cố gắng kiểm tra tất cả các ký tự xấu. Xem các liên kết sau: http://www.c-sharpcorner.com/UploadFile/prasad_1/RegExpPSD12062005021717AM/RegExpPSD.aspx http://www.windowsdevcenter.com/pub/a/oreilly/windows/news/csharp_01.html

Ngoài ra, thực hiện tìm kiếm cho "trình soạn thảo biểu thức chính quy", chúng giúp ích rất nhiều. Có một số xung quanh mà thậm chí xuất mã trong c # cho bạn.


Cho rằng .net là một khung công tác nhằm cho phép các chương trình chạy trên nhiều nền tảng (ví dụ Linux / Unix cũng như Windows), tôi cảm thấy Path.GetInvalidFileNameChars () là tốt nhất vì nó sẽ chứa kiến ​​thức về những gì là hoặc là ' t hợp lệ trên hệ thống tập tin mà chương trình của bạn đang được chạy. Ngay cả khi chương trình của bạn sẽ không bao giờ chạy trên Linux (có thể chứa đầy mã WPF), luôn có khả năng một số hệ thống tệp Windows mới sẽ xuất hiện trong tương lai và có các ký tự hợp lệ / không hợp lệ khác nhau. Xoay vòng của riêng bạn với regex là phát minh lại bánh xe và chuyển vấn đề nền tảng thành mã của riêng bạn.
Daniel Scott

Tôi đồng ý với lời khuyên của bạn về các biên tập viên / người kiểm tra regex trực tuyến. Tôi thấy chúng là vô giá (vì regex là những thứ khó khăn và đầy tinh tế có thể khiến bạn dễ dàng vượt qua, mang lại cho bạn một regex hành xử theo cách cực kỳ bất ngờ với các trường hợp cạnh). Yêu thích của tôi là regex101.com (Tôi thích cách nó phá vỡ regex xuống và cho bạn thấy rõ ràng những gì nó mong đợi để phù hợp). Tôi cũng khá thích debuggex.com vì nó có một đại diện trực quan nhỏ gọn của các nhóm đối sánh và các lớp nhân vật và không có gì.
Daniel Scott

3

Đây dường như là O (n) và không dành quá nhiều bộ nhớ cho chuỗi:

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

1
Tôi không nghĩ đó là O (n) khi bạn sử dụng chức năng 'Bất kỳ'.
II Mũi tên

@IIARWAYS và ý kiến ​​của bạn là gì?
Alexey F

Tôi không biết, tôi chỉ cảm thấy như vậy khi tôi viết bình luận của mình ... bây giờ tôi đã cố gắng tính toán nó, có vẻ như bạn đúng.
II Mũi tên

Tôi đã chọn cái này vì sự cân nhắc hiệu suất của bạn. Cảm ơn.
Berend Engelbrecht

3

Quét qua các câu trả lời ở đây, tất cả chúng ** dường như liên quan đến việc sử dụng một mảng char của các ký tự tên tệp không hợp lệ.

Cấp, điều này có thể tối ưu hóa vi mô - nhưng vì lợi ích của bất kỳ ai đang tìm cách kiểm tra một số lượng lớn các giá trị để trở thành tên tệp hợp lệ, đáng chú ý rằng việc xây dựng một băm các ký tự không hợp lệ sẽ mang lại hiệu suất tốt hơn đáng kể.

Tôi đã rất ngạc nhiên (bị sốc) trong quá khứ chỉ là một hashset (hoặc từ điển) nhanh hơn so với việc lặp lại trong một danh sách. Với các chuỗi, đó là một con số cực kỳ thấp (khoảng 5 - 7 mục từ bộ nhớ). Với hầu hết các dữ liệu đơn giản khác (tham chiếu đối tượng, số, v.v.), phép thuật chéo dường như có khoảng 20 mục.

Có 40 ký tự không hợp lệ trong "danh sách" Path.InvalidFileNameChars. Đã tìm kiếm ngày hôm nay và có một điểm chuẩn khá tốt ở đây trên StackOverflow cho thấy hashset sẽ mất hơn một nửa thời gian của một mảng / danh sách cho 40 mục: https://stackoverflow.com/a/10762995/949129

Đây là lớp người trợ giúp tôi sử dụng để vệ sinh đường dẫn. Bây giờ tôi quên tại sao tôi có tùy chọn thay thế lạ mắt trong đó, nhưng đó là một phần thưởng dễ thương.

Phương thức thưởng bổ sung "IsValidLocalPath" quá :)

(** những người không sử dụng biểu thức thông thường)

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

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

Bạn có thể sử dụng phương pháp rõ ràng.


2

Tên tệp không được chứa các ký tự từ Path.GetInvalidPathChars(), +#các biểu tượng, và tên cụ thể khác. Chúng tôi kết hợp tất cả các kiểm tra thành một lớp:

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

Phương pháp GetValidFileNamethay thế tất cả dữ liệu không chính xác _.


2

Một lớp lót để dọn dẹp chuỗi từ bất kỳ ký tự bất hợp pháp nào cho việc đặt tên tệp 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, "");

1
public static bool IsValidFilename(string testName)
{
    return !new Regex("[" + Regex.Escape(new String(System.IO.Path.GetInvalidFileNameChars())) + "]").IsMatch(testName);
}

0

Điều này sẽ làm bạn muốn, và tránh va chạm

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

    }

0

Tôi nghĩ rằng câu hỏi đã không được trả lời đầy đủ ... Các câu trả lời chỉ mô tả tên tệp sạch HOẶC đường dẫn ... không phải cả hai. Đây là giải pháp của tôi:

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

0

Tôi đã tạo một phương thức mở rộng kết hợp một số gợi ý:

  1. Giữ các ký tự không hợp lệ trong bộ băm
  2. Lọc các ký tự bên dưới ascii 127. Vì Path.GetInvalidFileNameChars không bao gồm tất cả các ký tự không hợp lệ có thể có mã ascii từ 0 đến 255. Xem tại đâyMSDN
  3. Khả năng xác định nhân vật thay thế

Nguồn:

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

0

Đây là một chức năng thay thế tất cả các ký tự không hợp lệ trong một tên tệp bằng một ký tự thay thế:

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

Ví dụ, dấu gạch dưới có thể được sử dụng làm ký tự thay thế:

NewFileName = ReplaceIllegalFileChars(FileName, '_');

Ngoài câu trả lời bạn đã cung cấp, vui lòng xem xét việc cung cấp giải thích ngắn gọn về lý do và cách khắc phục sự cố này.
jtate

-7

Hoặc bạn có thể làm

[YOUR STRING].Replace('\\', ' ').Replace('/', ' ').Replace('"', ' ').Replace('*', ' ').Replace(':', ' ').Replace('?', ' ').Replace('<', ' ').Replace('>', ' ').Replace('|', ' ').Trim();
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.