จะลบตัวละครที่ผิดกฎหมายออกจากเส้นทางและชื่อไฟล์ได้อย่างไร


456

ฉันต้องการวิธีที่แข็งแกร่งและเรียบง่ายในการลบพา ธ และอักขระไฟล์ที่ผิดกฎหมายออกจากสตริงอย่างง่าย ฉันใช้โค้ดด้านล่าง แต่ดูเหมือนจะไม่ทำอะไรเลยฉันหายไปอะไร

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
การตัดจะลบอักขระจากจุดเริ่มต้นและจุดสิ้นสุดของสตริง อย่างไรก็ตามคุณอาจถามว่าเพราะเหตุใดข้อมูลจึงไม่ถูกต้องแทนที่จะลองและฆ่าเชื้อ / แก้ไขข้อมูลปฏิเสธข้อมูล
7116

8
ชื่อสไตล์ Unix ไม่ถูกต้องบน Windows และฉันไม่ต้องการจัดการกับชื่อย่อ 8.3
Gary Willoughby

GetInvalidFileNameChars()จะตัดสิ่งที่ต้องการ: \ etc จากเส้นทางโฟลเดอร์
เจ้าหมอ CAD

1
Path.GetInvalidPathChars()ดูเหมือนจะไม่ตัด*หรือ?
เจ้าหมอ CAD

19
ฉันทดสอบห้าคำตอบจากคำถามนี้ (วนซ้ำที่กำหนดเวลา 100,000) และวิธีการต่อไปนี้เร็วที่สุด การแสดงออกปกติเกิดขึ้นที่ 2 และช้าลง 25%: public string GetSafeFilename (ชื่อไฟล์สตริง) {return string.Join ("_", filename.Split (Path.GetInvalidFileNameChars ()); }
Brain2000

คำตอบ:


494

ลองทำสิ่งนี้แทน

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

แต่ฉันต้องเห็นด้วยกับความคิดเห็นที่ฉันอาจพยายามที่จะจัดการกับแหล่งที่มาของเส้นทางที่ผิดกฎหมายมากกว่าที่จะพยายามที่จะคล้ำเส้นทางที่ผิดกฎหมายให้ถูกต้องตามกฎหมาย แต่อาจจะไม่ได้ตั้งใจ

แก้ไข: หรือโซลูชันที่ 'ดีกว่า' โดยใช้ 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, "");

ยังคงเป็นคำถามที่จะถามทำไมคุณทำเช่นนี้ในสถานที่แรก


40
ไม่จำเป็นต้องผนวกสองรายการเข้าด้วยกัน รายการถ่านชื่อไฟล์ที่ผิดกฎหมายมีรายการถ่านเส้นทางที่ผิดกฎหมายและมีอีกไม่กี่ นี่คือรายการของทั้งสองรายการที่ส่งไปยัง int: 34,60,62,124,0,1,2,3,4,,5,6,7,8,9,10,11,12,13,14,14,15,16, 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,58,42,63,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 สิ่งนี้อาจเป็นจริงใน Windows และการใช้งาน. NET ของ Microsoft ฉันไม่เต็มใจที่จะตั้งสมมติฐานแบบเดียวกันสำหรับการใช้โมโนพูดกับ Linux
Matthew Scharley

7
เกี่ยวกับทางออกแรก ไม่ควร StringBuilder จะมีประสิทธิภาพมากกว่าการกำหนดสตริง
epignosisx

6
สำหรับสิ่งที่คุ้มค่า @MatthewScharley การใช้งาน Mono ของ GetInvalidPathChars () จะส่งกลับเฉพาะ 0x00 และ GetInvalidFileNameChars () จะส่งกลับเฉพาะ 0x00 และ '/' เมื่อเรียกใช้บนแพลตฟอร์มที่ไม่ใช่ Windows บน Windows รายการของอักขระที่ไม่ถูกต้องจะยาวขึ้นและ GetInvalidPathChars () จะซ้ำกันทั้งหมดภายใน GetInvalidFileNameChars () สิ่งนี้จะไม่เปลี่ยนแปลงในอนาคตอันใกล้ดังนั้นสิ่งที่คุณทำจริงๆคือการเพิ่มระยะเวลาที่ฟังก์ชั่นนี้ใช้เป็นสองเท่าเพราะคุณกังวลว่าคำจำกัดความของเส้นทางที่ถูกต้องจะมีการเปลี่ยนแปลงในไม่ช้า ซึ่งมันจะไม่
Warren Rumak

13
@Charleh การสนทนานี้ไม่จำเป็น ... รหัสควรได้รับการปรับให้เหมาะสมเสมอและไม่มีความเสี่ยงที่จะเกิดข้อผิดพลาด ชื่อไฟล์ก็เป็นส่วนหนึ่งของเส้นทางด้วยเช่นกัน ดังนั้นจึงเป็นเพียงเหตุผลที่GetInvalidPathChars()อาจมีตัวละครที่GetInvalidFileNameChars()จะไม่ คุณไม่ได้ใช้ความถูกต้องมากกว่าการเพิ่มประสิทธิภาพ "ก่อนกำหนด" คุณเพียงแค่ใช้รหัสที่ไม่ดี
สเตฟานเฟเบียน

356

คำถามเดิมขอให้ "ลบอักขระผิดกฎหมาย":

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

คุณอาจต้องการแทนที่:

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

คำตอบนี้เป็นอีกหัวข้อโดยเซเรสฉันชอบมันที่เรียบง่ายและเรียบง่าย


10
ในการตอบคำถามของ OP อย่างแม่นยำคุณจะต้องใช้ "" แทนที่จะเป็น "_" แต่คำตอบของคุณอาจนำไปใช้กับเราในทางปฏิบัติมากขึ้น ฉันคิดว่าการแทนที่ตัวละครที่ผิดกฎหมายด้วยตัวละครที่ถูกกฎหมายนั้นทำได้บ่อยกว่า
BH

37
ฉันทดสอบห้าวิธีจากคำถามนี้ (หมดเวลา 100,000) และวิธีนี้เป็นวิธีที่เร็วที่สุด การแสดงออกปกติเกิดขึ้นที่ 2 และช้ากว่าวิธีนี้ 25%
Brain2000

10
เพื่อที่จะแสดงความคิดเห็นของ @BH คุณสามารถใช้ string.Concat (name.Split (Path.GetInvalidFileNameChars ()))
Michael Sutton

210

ฉันใช้ Linq เพื่อล้างชื่อไฟล์ คุณสามารถขยายได้อย่างง่ายดายเพื่อตรวจสอบเส้นทางที่ถูกต้องเช่นกัน

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

ปรับปรุง

ความคิดเห็นบางส่วนระบุว่าวิธีนี้ใช้ไม่ได้สำหรับพวกเขาดังนั้นฉันจึงได้รวมลิงค์ไปยังตัวอย่างข้อมูล DotNetFiddle ดังนั้นคุณอาจตรวจสอบวิธีการได้

https://dotnetfiddle.net/nw1SWY


4
สิ่งนี้ไม่ได้ผลสำหรับฉัน วิธีนี้ไม่ได้ส่งคืนสตริงใหม่ทั้งหมด มันกำลังส่งคืนชื่อไฟล์ที่ส่งผ่านตามที่เป็นอยู่
Karan

สิ่งที่ @Karan พูดมันไม่ทำงานสตริงเดิมกลับมา
Jon

จริงๆคุณสามารถทำเช่นนี้กับ Linq var invalid = new HashSet<char>(Path.GetInvalidPathChars()); return new string(originalString.Where(s => !invalid.Contains(s)).ToArray())เช่นนี้แม้ว่า: ประสิทธิภาพอาจไม่ยอดเยี่ยม แต่อาจไม่สำคัญ
Casey

2
@Karan หรือ Jon คุณป้อนฟังก์ชั่นนี้คืออะไร? ดูการแก้ไขของฉันสำหรับการยืนยันวิธีนี้
Michael Minton

3
เป็นเรื่องง่าย - พวกมันผ่านสายอักขระที่ถูกต้องแล้ว โหวตขึ้นสำหรับโซลูชันการรวมที่ยอดเยี่ยม
Nickmaovich

89

คุณสามารถลบตัวอักษรที่ผิดกฎหมายโดยใช้ Linq ดังนี้:

var invalidChars = Path.GetInvalidFileNameChars();

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

แก้ไข
นี่คือลักษณะที่มีการแก้ไขตามที่ระบุไว้ในความคิดเห็น:

var invalidChars = Path.GetInvalidFileNameChars();

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

1
ฉันชอบวิธีนี้: คุณเก็บเฉพาะตัวอักษรที่ได้รับอนุญาตในสตริง (ซึ่งไม่มีอะไรอื่นนอกจากอาร์เรย์ถ่าน)
Dude Pascalou

6
ฉันรู้ว่านี่เป็นคำถามเก่า แต่นี่เป็นคำตอบที่ยอดเยี่ยม อย่างไรก็ตามฉันต้องการเพิ่มใน c # คุณไม่สามารถแปลงจากอักขระ char [] เป็นสตริงได้ทั้งโดยปริยายหรือชัดแจ้ง (บ้ารู้จริง) ดังนั้นคุณจะต้องวางลงในตัวสร้างสตริง
JNYRanger

1
ฉันไม่ได้ยืนยันสิ่งนี้ แต่ฉันคาดว่า Path.GetInvalidPathChars () จะเป็น superset ของ GetInvalidFileNameChars () และครอบคลุมทั้งชื่อไฟล์และเส้นทางดังนั้นฉันอาจจะใช้แทน
angularsen

3
@anjdreas จริง ๆ แล้ว Path.GetInvalidPathChars () น่าจะเป็นส่วนย่อยของ Path.GetInvalidFileNameChars () ไม่ใช่วิธีอื่น ๆ Path.GetInvalidPathChars () จะไม่ส่งคืน '?' ตัวอย่างเช่น
Rafael Costa

1
นี่เป็นคำตอบที่ดี ฉันใช้ทั้งชื่อไฟล์และรายการไฟล์พา ธ : ____________________________ สตริง cleanData = สตริงใหม่ (data.Where (x =>! Path.GetInvalidFileNameChars (). ประกอบด้วย (x) &&! Path.GetInvalidPathChars (). toArray ());
goamn

27

สิ่งเหล่านี้ล้วนเป็นวิธีแก้ปัญหาที่ยอดเยี่ยม แต่ทั้งหมดล้วนแล้วแต่วางใจPath.GetInvalidFileNameCharsซึ่งอาจไม่น่าเชื่อถือเท่าที่คุณคิด สังเกตคำพูดต่อไปนี้ในเอกสาร MSDN เมื่อPath.GetInvalidFileNameChars:

อาร์เรย์ที่ส่งคืนจากวิธีนี้ไม่รับประกันว่าจะมีชุดอักขระทั้งหมดที่ไม่ถูกต้องในชื่อไฟล์และไดเรกทอรี ชุดอักขระที่ไม่ถูกต้องอาจแตกต่างกันไปตามระบบไฟล์ ตัวอย่างเช่นบนแพลตฟอร์มเดสก์ท็อปที่ใช้ Windows อักขระพา ธ ที่ไม่ถูกต้องอาจมีอักขระ ASCII / Unicode 1 ถึง 31 เช่นเดียวกับ quote (") น้อยกว่า (<) มากกว่า (>), pipe (|), backspace ( \ b), null (\ 0) และแท็บ (\ t)

มันไม่ได้ดีไปกว่านี้ด้วยPath.GetInvalidPathCharsวิธีการ มันมีคำพูดเดียวกันแน่นอน


13
แล้วจุดของ Path.GetInvalidFileNameChars คืออะไร? ฉันคาดหวังว่ามันจะส่งคืนอักขระที่ไม่ถูกต้องสำหรับระบบปัจจุบันโดยอาศัย. NET เพื่อทราบว่าระบบไฟล์ใดที่ฉันกำลังใช้อยู่และนำเสนอตัวอักษรที่ไม่ถูกต้องที่เหมาะสม หากไม่เป็นเช่นนั้นและเพิ่งคืนอักขระ hardcoded ซึ่งไม่น่าเชื่อถือตั้งแต่แรกวิธีการนี้ควรถูกลบเนื่องจากมีค่าเป็นศูนย์
Jan

1
ฉันรู้ว่านี่เป็นความคิดเห็นเก่า แต่ @Jan คุณอาจต้องการที่จะเขียนในระบบไฟล์อื่นบางทีนี่อาจเป็นเหตุผลที่มีคำเตือน
fantastik78

3
@ จุดดี fantastik78 แต่ในกรณีนี้ฉันต้องการอาร์กิวเมนต์เพิ่มเติม enum เพื่อระบุ FS ระยะไกลของฉัน หากนี่คือความพยายามในการบำรุงรักษาที่มากเกินไป (ซึ่งเป็นกรณีที่น่าจะเป็นไปได้มากที่สุด) วิธีการทั้งหมดนี้ยังคงเป็นความคิดที่ไม่ดีเพราะมันทำให้คุณรู้สึกถึงความปลอดภัยผิด
มกราคม

1
@Jan ฉันเห็นด้วยกับคุณโดยสิ้นเชิงฉันแค่โต้เถียงเกี่ยวกับคำเตือน
fantastik78

น่าสนใจนี่คือตัวอักษรที่ไม่ถูกต้อง "บัญชีดำ" "บัญชีปลอดภัย" จะดีกว่าเฉพาะตัวอักษรที่ใช้ได้จริงหรือไม่ เตือนฉันถึงความคิด "virusscanner" ที่โง่แทนที่จะอนุญาตแอปที่อนุญาตพิเศษ ....
Bernhard

26

สำหรับชื่อไฟล์:

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

สำหรับเส้นทางแบบเต็ม:

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

โปรดทราบว่าหากคุณตั้งใจจะใช้สิ่งนี้เป็นคุณลักษณะด้านความปลอดภัยแนวทางที่มีประสิทธิภาพยิ่งกว่าก็คือการขยายเส้นทางทั้งหมดแล้วตรวจสอบว่าเส้นทางที่ผู้ใช้ระบุนั้นเป็นลูกของไดเรกทอรีที่ผู้ใช้ควรมีสิทธิ์เข้าถึง


18

สำหรับ starters Trim จะลบอักขระออกจากจุดเริ่มต้นหรือจุดสิ้นสุดของสตริงเท่านั้น ประการที่สองคุณควรประเมินว่าคุณต้องการลบตัวละครที่น่ารังเกียจหรือล้มเหลวอย่างรวดเร็วและให้ผู้ใช้ทราบว่าชื่อไฟล์ไม่ถูกต้อง ตัวเลือกของฉันเป็นตัวหลัง แต่อย่างน้อยคำตอบของฉันควรแสดงให้คุณเห็นว่าจะทำสิ่งที่ถูกและผิด:

คำถาม StackOverflow แสดงวิธีการตรวจสอบว่าสตริงที่กำหนดเป็นชื่อไฟล์ที่ถูกต้อง หมายเหตุคุณสามารถใช้ regex จากคำถามนี้เพื่อลบอักขระด้วยการแทนที่นิพจน์ปกติ (ถ้าคุณต้องการทำสิ่งนี้จริงๆ)


ฉันเห็นด้วยอย่างยิ่งกับคำแนะนำที่สอง
OregonGhost

4
ปกติฉันจะเห็นด้วยกับที่สอง แต่ฉันมีโปรแกรมที่สร้างชื่อไฟล์และอาจมีตัวละครที่ผิดกฎหมายในบางสถานการณ์ เนื่องจากโปรแกรมของฉันกำลังสร้างชื่อไฟล์ที่ผิดกฎหมายฉันคิดว่ามันเหมาะสมที่จะลบ / แทนที่ตัวละครเหล่านั้น (เพียงชี้ให้เห็นถึงกรณีการใช้งานที่ถูกต้อง)
JDB ยังคงจำโมนิกา

16

วิธีที่ดีที่สุดในการลบอักขระที่ผิดกฎหมายจากอินพุตของผู้ใช้คือการแทนที่อักขระที่ผิดกฎหมายโดยใช้คลาส Regex สร้างวิธีในโค้ดด้านหลังหรือตรวจสอบที่ฝั่งไคลเอ็นต์โดยใช้การควบคุม

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

หรือ

<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 โซลูชันนี้ดีกว่าอย่างอื่นแทนที่จะค้นหาตัวอักษรที่ไม่ถูกต้องทั้งหมดให้กำหนดว่าอะไรถูกต้อง
igorushi

15

ฉันใช้นิพจน์ทั่วไปเพื่อให้บรรลุสิ่งนี้ ก่อนอื่นฉันสร้าง regex แบบไดนามิก

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

จากนั้นฉันก็เรียก removeInvalidChars แทนที่เพื่อทำการค้นหาและแทนที่ สิ่งนี้สามารถขยายได้อย่างชัดเจนเพื่อให้ครอบคลุมตัวอักษรเส้นทาง


มันแปลกสำหรับฉัน ฉันจะตรวจสอบอีกครั้งเมื่อฉันมีโอกาส คุณจะเจาะจงมากขึ้นและอธิบายสิ่งที่ไม่ได้ผลสำหรับคุณ
Jeff Yates

1
มันจะไม่ทำงาน (อย่างน้อยที่สุด) เพราะคุณไม่ได้หลบหนีจากตัวละครอย่างถูกต้องและบางคนก็มีความหมายพิเศษ อ้างถึงคำตอบของฉันสำหรับวิธีการทำเช่นนั้น
Matthew Scharley

@Jeff: เวอร์ชันของคุณยังดีกว่าของ Matthew อยู่ถ้าคุณปรับเปลี่ยนเล็กน้อย อ้างถึงคำตอบของฉันเกี่ยวกับวิธีการ
Jan

2
ฉันยังจะเพิ่มรูปแบบชื่อไฟล์ที่ไม่ถูกต้องอื่น ๆ ที่สามารถพบได้ในMSDNและขยายโซลูชันของคุณไปยัง regex ต่อไปนี้: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

ฉันชอบแนวคิดของ Jeff Yates มันจะทำงานได้อย่างสมบูรณ์แบบหากคุณปรับเปลี่ยนเล็กน้อย:

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

การปรับปรุงเพียงเพื่อหลีกเลี่ยง regex ที่สร้างขึ้นโดยอัตโนมัติ


11

นี่คือข้อมูลโค้ดที่น่าจะช่วยได้สำหรับ. NET 3 และสูงกว่า

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

วิธีแก้ปัญหาส่วนใหญ่รวมตัวอักษรที่ผิดกฎหมายทั้งเส้นทางและชื่อไฟล์ที่ผิด (แม้ว่าทั้งสองสายปัจจุบันจะส่งคืนชุดอักขระเดียวกัน) ฉันจะแยก path + filename ใน path และ filename ก่อนจากนั้นให้ใช้การตั้งค่าที่เหมาะสมกับทั้งสองถ้ารวมกันทั้งสองอีกครั้ง

wvd_vegt


+1: จริงมาก วันนี้ทำงานใน. NET 4.0, โซลูชัน regex จากคำตอบยอดนิยม nuked backslashes ทั้งหมดในเส้นทางแบบเต็ม ดังนั้นฉันจึงทำ regex สำหรับเส้นทาง dir และ regex เพียงชื่อไฟล์ทำความสะอาดแยกจากกันและรวมกันอีกครั้ง
dario_ramos

อาจเป็นจริง แต่ไม่ตอบคำถาม ฉันไม่แน่ใจว่าคลุมเครือ 'ฉันจะทำแบบนี้' เป็นประโยชน์อย่างมากเมื่อเทียบกับโซลูชั่นที่มีอยู่แล้วในที่นี้ (ดูตัวอย่างคำตอบของลิลลี่ด้านล่าง)
Ian Grainger

6

หากคุณลบหรือแทนที่ด้วยอักขระที่ไม่ถูกต้องอักขระเดียวคุณสามารถมีการชนกันได้:

<abc -> abc
>abc -> abc

นี่เป็นวิธีง่ายๆในการหลีกเลี่ยงสิ่งนี้:

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

ผลลัพธ์:

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


4

ฉันเขียนสัตว์ประหลาดตัวนี้เพื่อความสนุกมันช่วยให้คุณไปกลับ:

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
ฉันชอบสิ่งนี้เพราะมันหลีกเลี่ยงการมีสองสายที่แตกต่างกันในการสร้างเส้นทางผลลัพธ์เดียวกัน
Kim

3

ฉันคิดว่ามันง่ายกว่าในการตรวจสอบโดยใช้ regex และ specifiing ตัวละครที่ได้รับอนุญาตแทนที่จะพยายามตรวจสอบตัวละครที่ไม่ดีทั้งหมด ดูลิงค์เหล่านี้: http://www.c-sharpcorner.com/UploadFile/prasad_1/RegExpPSD12062005021717AM/RegExpPSD.aspx http://www.windowsdevcenter.com/pub/a/oreilly/windows/news/csharp_0101.html

นอกจากนี้ทำการค้นหา "เครื่องมือแก้ไขนิพจน์ทั่วไป" พวกเขาช่วยได้มาก มีบางอย่างที่แม้แต่เอาท์พุทรหัสใน c # สำหรับคุณ


ระบุว่า. net เป็นเฟรมเวิร์กที่มีวัตถุประสงค์เพื่อให้โปรแกรมทำงานบนหลายแพลตฟอร์ม (เช่น Linux / Unix รวมถึง Windows) ฉันรู้สึกว่า Path.GetInvalidFileNameChars () นั้นดีที่สุดเนื่องจากมันจะมีความรู้ว่าอะไรคืออะไรหรือไม่ ' ไม่ถูกต้องในระบบไฟล์ที่โปรแกรมของคุณกำลังทำงานอยู่ แม้ว่าโปรแกรมของคุณจะไม่ทำงานบน Linux (อาจจะเต็มไปด้วยรหัส WPF) แต่ก็มีโอกาสที่ระบบไฟล์ Windows ใหม่ ๆ จะเข้ามาในอนาคตและมีตัวอักษรที่ถูกต้อง / ไม่ถูกต้องอยู่เสมอ การพลิกกลับของคุณเองด้วย regex เป็นการพลิกโฉมวงล้อและเปลี่ยนปัญหาแพลตฟอร์มเป็นรหัสของคุณเอง
Daniel Scott

ฉันเห็นด้วยกับคำแนะนำของคุณเกี่ยวกับบรรณาธิการ / ผู้ทดสอบออนไลน์ของ regex ฉันพบว่ามันมีคุณค่า (เนื่องจาก regexes เป็นสิ่งที่ยุ่งยากและเต็มไปด้วยความละเอียดอ่อนที่สามารถเดินทางไปหาคุณได้อย่างง่ายดายทำให้คุณมี regex ที่ทำงานอย่างไม่คาดคิดกับกรณีขอบ) สิ่งที่ฉันชอบคือregex101.com (ฉันชอบวิธีที่ทำให้ regex พังลงและแสดงให้คุณเห็นอย่างชัดเจนถึงสิ่งที่คาดว่าจะจับคู่) ฉันค่อนข้างชอบdebuggex.comเพราะมันมีภาพที่กะทัดรัดของกลุ่มการแข่งขันและคลาสของตัวละครและอะไรก็ตาม
Daniel Scott

3

ดูเหมือนว่าจะเป็น O (n) และไม่ใช้หน่วยความจำมากเกินไปกับสตริง:

    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
ฉันไม่คิดว่ามันเป็น O (n) เมื่อคุณใช้ฟังก์ชั่น 'ใด ๆ '
ลูกศรที่สอง

@IIARROWS และคุณคิดอย่างไร?
Alexey F

ฉันไม่รู้มันแค่ไม่รู้สึกอย่างนั้นเมื่อฉันเขียนความคิดเห็นของฉัน ... ตอนนี้ฉันพยายามคำนวณมันดูเหมือนว่าคุณพูดถูก
ลูกศรที่สอง

ฉันเลือกอันนี้เนื่องจากการพิจารณาประสิทธิภาพของคุณ ขอบคุณ
Berend Engelbrecht

3

การสแกนคำตอบที่นี่พวกเขาทั้งหมด ** ดูเหมือนว่าจะเกี่ยวข้องกับการใช้อักขระชื่อไฟล์ที่ไม่ถูกต้อง

จริงอยู่นี่อาจเป็นการเพิ่มประสิทธิภาพแบบไมโคร - แต่เพื่อประโยชน์ของทุกคนที่อาจต้องการตรวจสอบค่าจำนวนมากสำหรับการเป็นชื่อไฟล์ที่ถูกต้องเป็นเรื่องที่น่าสังเกตว่าการสร้างแฮชเซ็ตของอักขระที่ไม่ถูกต้องจะทำให้ประสิทธิภาพดีขึ้นอย่างมาก

ฉันเคยประหลาดใจมาก (ตกใจ) ในอดีตที่ผ่านมาว่าแฮชเซท (หรือพจนานุกรม) เร็วกว่าการทำซ้ำมากกว่ารายการ ด้วยสตริงมันเป็นตัวเลขที่ต่ำมาก (ประมาณ 5-7 รายการจากหน่วยความจำ) ด้วยข้อมูลทั่วไปอื่น ๆ (การอ้างอิงวัตถุตัวเลข ฯลฯ ) ครอสโอเวอร์เวทย์มนตร์ดูเหมือนจะมีประมาณ 20 รายการ

มี 40 อักขระที่ไม่ถูกต้องใน Path.InvalidFileNameChars "list" ทำการค้นหาในวันนี้และมีเกณฑ์มาตรฐานที่ดีใน StackOverflow ที่แสดง hashset จะใช้เวลาครึ่งหนึ่งของอาร์เรย์ / รายการ 40 รายการ: https://stackoverflow.com/a/10762995/949129

นี่คือคลาสตัวช่วยที่ฉันใช้สำหรับเส้นทางการฆ่าเชื้อ ตอนนี้ฉันลืมไปแล้วว่าทำไมฉันถึงมีตัวเลือกการเปลี่ยนแฟนซี แต่มีโบนัสน่ารัก

วิธีโบนัสเพิ่มเติม "IsValidLocalPath" ด้วย :)

(** ผู้ที่ไม่ได้ใช้นิพจน์ทั่วไป)

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

คุณสามารถใช้วิธีการได้อย่างชัดเจน


2

ชื่อไฟล์ไม่สามารถมีตัวละครจากPath.GetInvalidPathChars(), +และ#สัญลักษณ์และชื่อเฉพาะอื่น ๆ เรารวมการตรวจสอบทั้งหมดเป็นหนึ่งคลาส:

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

วิธีการแทนที่ข้อมูลไม่ถูกต้องทั้งหมดเพื่อGetValidFileName_


2

ซับหนึ่งไปยังสตริงการล้างข้อมูลจาก chars ที่ผิดกฎหมายสำหรับการตั้งชื่อไฟล์ 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

สิ่งนี้จะต้องการคุณและหลีกเลี่ยงการชน

 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

ฉันคิดว่าคำถามยังไม่ได้ตอบคำถามทั้งหมด ... คำตอบอธิบายชื่อไฟล์หรือเส้นทางที่สะอาดเท่านั้นไม่ใช่ทั้งสองอย่าง นี่คือทางออกของฉัน:

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

ฉันสร้างวิธีส่วนขยายที่รวมคำแนะนำหลายข้อไว้ด้วยกัน:

  1. การถืออักขระผิดกฎหมายไว้ในชุดแฮช
  2. การกรองอักขระด้านล่าง ascii 127 เนื่องจาก Path.GetInvalidFileNameChars ไม่มีอักขระที่ไม่ถูกต้องทั้งหมดที่เป็นไปได้ด้วยรหัส ascii ตั้งแต่ 0 ถึง 255 ดูที่นี่และ MSDN
  3. มีความเป็นไปได้ที่จะกำหนดตัวละครทดแทน

ที่มา:

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

นี่คือฟังก์ชันที่แทนที่อักขระที่ผิดกฎหมายทั้งหมดในชื่อไฟล์ด้วยอักขระที่แทนที่:

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

ตัวอย่างเช่นขีดล่างสามารถใช้เป็นอักขระแทน:

NewFileName = ReplaceIllegalFileChars(FileName, '_');

นอกเหนือจากคำตอบที่คุณให้มาโปรดพิจารณาให้คำอธิบายสั้น ๆ ถึงสาเหตุและวิธีการนี้ในการแก้ไขปัญหา
jtate

-7

หรือคุณสามารถทำได้

[YOUR STRING].Replace('\\', ' ').Replace('/', ' ').Replace('"', ' ').Replace('*', ' ').Replace(':', ' ').Replace('?', ' ').Replace('<', ' ').Replace('>', ' ').Replace('|', ' ').Trim();
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.