ฉันมีสตริงเช่น "Foo: Bar" ที่ฉันต้องการใช้เป็นชื่อไฟล์ แต่ใน Windows ไม่อนุญาตให้ใช้อักขระ ":" ในชื่อไฟล์
มีวิธีที่จะเปลี่ยน "Foo: Bar" ให้เป็น "Foo-Bar" ได้หรือไม่?
ฉันมีสตริงเช่น "Foo: Bar" ที่ฉันต้องการใช้เป็นชื่อไฟล์ แต่ใน Windows ไม่อนุญาตให้ใช้อักขระ ":" ในชื่อไฟล์
มีวิธีที่จะเปลี่ยน "Foo: Bar" ให้เป็น "Foo-Bar" ได้หรือไม่?
คำตอบ:
ลองทำสิ่งนี้:
string fileName = "something";
foreach (char c in System.IO.Path.GetInvalidFileNameChars())
{
fileName = fileName.Replace(c, '_');
}
แก้ไข:
เนื่องจากGetInvalidFileNameChars()
จะส่งคืน 10 หรือ 15 ตัวอักษรจึงควรใช้ a StringBuilder
แทนสตริงธรรมดา เวอร์ชันดั้งเดิมจะใช้เวลานานกว่าและใช้หน่วยความจำมากขึ้น
file.name.txt.pdf
เป็น pdf ที่ถูกต้อง Windows อ่านค่าสุดท้าย.
ของส่วนขยายเท่านั้น
fileName = fileName.Replace(":", "-")
อย่างไรก็ตาม ":" ไม่ใช่อักขระที่ผิดกฎหมายเพียงตัวเดียวสำหรับ Windows คุณจะต้องจัดการ:
/, \, :, *, ?, ", <, > and |
สิ่งเหล่านี้มีอยู่ใน System.IO.Path.GetInvalidFileNameChars ();
นอกจากนี้ (บน Windows), "." ไม่สามารถเป็นอักขระเดียวในชื่อไฟล์ (ทั้ง ".", ".. ", "... " และอื่น ๆ ไม่ถูกต้อง) โปรดใช้ความระมัดระวังในการตั้งชื่อไฟล์ด้วย "." เช่น
echo "test" > .test.
จะสร้างไฟล์ชื่อ ".test"
สุดท้ายหากคุณจริงๆต้องการที่จะทำสิ่งที่ถูกต้องมีบางชื่อไฟล์พิเศษที่คุณต้องมองออกไปสำหรับ ใน Windowsคุณไม่สามารถสร้างไฟล์ชื่อ:
CON, PRN, AUX, CLOCK$, NUL
COM0, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9
LPT0, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, and LPT9.
สิ่งนี้ไม่ได้มีประสิทธิภาพมากขึ้น แต่สนุกกว่า :)
var fileName = "foo:bar";
var invalidChars = System.IO.Path.GetInvalidFileNameChars();
var cleanFileName = new string(fileName.Where(m => !invalidChars.Contains(m)).ToArray<char>());
ในกรณีที่ใครต้องการรุ่นที่ปรับให้เหมาะสมStringBuilder
ให้ใช้สิ่งนี้ รวมเคล็ดลับของ rkagererเป็นตัวเลือก
static char[] _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>
public static string MakeValidFileName(string text, char? replacement = '_', bool fancy = true)
{
StringBuilder sb = new StringBuilder(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 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;
}
นี่เป็นการบิดเล็กน้อยสำหรับคำตอบของ Diego
หากคุณไม่กลัว Unicode คุณสามารถรักษาความเที่ยงตรงได้มากขึ้นโดยการแทนที่อักขระที่ไม่ถูกต้องด้วยสัญลักษณ์ Unicode ที่ถูกต้องซึ่งมีลักษณะคล้ายกับสัญลักษณ์เหล่านั้น นี่คือรหัสที่ฉันใช้ในโครงการล่าสุดเกี่ยวกับรายการตัดไม้:
static string MakeValidFilename(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 slash
foreach (char c in System.IO.Path.GetInvalidFileNameChars()) {
text = text.Replace(c, '_');
}
return text;
}
สิ่งนี้สร้างชื่อไฟล์เช่น1⁄2” spruce.txt
แทนที่จะเป็น1_2_ spruce.txt
ใช่มันใช้งานได้จริง:
Caveat Emptor
ฉันรู้ว่าเคล็ดลับนี้ใช้ได้กับ NTFS แต่รู้สึกประหลาดใจที่พบว่ามันใช้ได้กับพาร์ติชัน FAT และ FAT32 ด้วย นั่นเป็นเพราะชื่อไฟล์แบบยาวจะถูกเก็บไว้ใน Unicodeแม้ว่าจะย้อนกลับไปถึง Windows 95 / NT ก็ตาม ฉันทดสอบบน Win7, XP และแม้แต่เราเตอร์ที่ใช้ Linux และพวกเขาก็ปรากฏว่าตกลง ไม่สามารถพูดเช่นเดียวกันกับภายใน DOSBox
ที่กล่าวว่าก่อนที่คุณจะเลิกใช้สิ่งนี้ให้พิจารณาว่าคุณต้องการความซื่อสัตย์เป็นพิเศษหรือไม่ ยูนิโค้ดดูลุคอาจสร้างความสับสนให้คนหรือโปรแกรมเก่าเช่นเก่าของ OS อาศัยcodepages
นี่คือเวอร์ชันของคำตอบที่ยอมรับLinq
ซึ่งใช้Enumerable.Aggregate
:
string fileName = "something";
Path.GetInvalidFileNameChars()
.Aggregate(fileName, (current, c) => current.Replace(c, '_'));
ดิเอโกมีวิธีแก้ปัญหาที่ถูกต้อง แต่มีข้อผิดพลาดเล็กน้อยอยู่ในนั้น เวอร์ชันของสตริงการแทนที่ที่ใช้ควรเป็นสตริงแทนที่ (char, char) ไม่มีสตริงแทนที่ (char, string)
ฉันไม่สามารถแก้ไขคำตอบได้หรือฉันเพิ่งทำการเปลี่ยนแปลงเล็กน้อย
ดังนั้นควรเป็น:
string fileName = "something";
foreach (char c in System.IO.Path.GetInvalidFileNameChars())
{
fileName = fileName.Replace(c, '_');
}
นี่คือเวอร์ชันที่ใช้StringBuilder
และIndexOfAny
ผนวกรวมเพื่อประสิทธิภาพสูงสุด นอกจากนี้ยังส่งคืนสตริงเดิมแทนที่จะสร้างสตริงที่ซ้ำกัน
สุดท้าย แต่ไม่ท้ายสุดมันมีคำสั่ง switch ที่ส่งคืนอักขระที่เหมือนกันซึ่งคุณสามารถปรับแต่งได้ตามที่คุณต้องการ ตรวจสอบการค้นหาความสับสนของ Unicode.orgเพื่อดูว่าคุณมีตัวเลือกอะไรบ้างขึ้นอยู่กับแบบอักษร
public static string GetSafeFilename(string arbitraryString)
{
var invalidChars = System.IO.Path.GetInvalidFileNameChars();
var replaceIndex = arbitraryString.IndexOfAny(invalidChars, 0);
if (replaceIndex == -1) return arbitraryString;
var r = new StringBuilder();
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();
}
มันไม่ได้ตรวจสอบ.
, ..
หรือชื่อที่สงวนชอบCON
เพราะมันไม่ชัดเจนว่าการเปลี่ยนที่ควรจะเป็น
ทำความสะอาดโค้ดของฉันเล็กน้อยและทำการ refactoring เล็กน้อย ... ฉันสร้างส่วนขยายสำหรับประเภทสตริง:
public static string ToValidFileName(this string s, char replaceChar = '_', char[] includeChars = null)
{
var invalid = Path.GetInvalidFileNameChars();
if (includeChars != null) invalid = invalid.Union(includeChars).ToArray();
return string.Join(string.Empty, s.ToCharArray().Select(o => o.In(invalid) ? replaceChar : o));
}
ตอนนี้ใช้งานง่ายขึ้นด้วย:
var name = "Any string you want using ? / \ or even +.zip";
var validFileName = name.ToValidFileName();
หากคุณต้องการแทนที่ด้วยอักขระอื่นที่ไม่ใช่ "_" คุณสามารถใช้:
var validFileName = name.ToValidFileName(replaceChar:'#');
และคุณสามารถเพิ่มตัวอักษรเพื่อแทนที่ .. ตัวอย่างเช่นคุณไม่ต้องการเว้นวรรคหรือลูกน้ำ:
var validFileName = name.ToValidFileName(includeChars: new [] { ' ', ',' });
หวังว่ามันจะช่วย ...
ไชโย
อีกวิธีง่ายๆ:
private string MakeValidFileName(string original, char replacementChar = '_')
{
var invalidChars = new HashSet<char>(Path.GetInvalidFileNameChars());
return new string(original.Select(c => invalidChars.Contains(c) ? replacementChar : c).ToArray());
}
รหัสง่ายๆเพียงบรรทัดเดียว:
var validFileName = Path.GetInvalidFileNameChars().Aggregate(fileName, (f, c) => f.Replace(c, '_'));
คุณสามารถห่อด้วยวิธีการขยายหากคุณต้องการใช้ซ้ำ
public static string ToValidFileName(this string fileName) => Path.GetInvalidFileNameChars().Aggregate(fileName, (f, c) => f.Replace(c, '_'));
ฉันต้องการระบบที่ไม่สามารถสร้างการชนกันได้ดังนั้นฉันจึงไม่สามารถจับคู่อักขระหลายตัวเป็นตัวเดียวได้ ฉันลงเอยด้วย:
public static class Extension
{
/// <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>
private static readonly HashSet<char> CleanFileNameChars = new HashSet<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>
public static string CleanFileName(this string dirtyFileName)
{
string EscapeHexString(char c) =>
"{" + (c > 255 ? $"{(uint)c:X4}" : $"{(uint)c:X2}") + "}";
return string.Join(string.Empty,
dirtyFileName.Select(
c =>
c == '{' ? "{{" :
c == '}' ? "}}" :
CleanFileNameChars.Contains(c) ? $"{c}" :
EscapeHexString(c)));
}
}
ฉันต้องทำวันนี้ ... ในกรณีของฉันฉันต้องเชื่อมชื่อลูกค้ากับวันที่และเวลาสำหรับไฟล์. kmz สุดท้าย ทางออกสุดท้ายของฉันคือ:
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));
คุณสามารถทำให้มันแทนที่ช่องว่างได้หากคุณเพิ่มอักขระช่องว่างลงในอาร์เรย์ที่ไม่ถูกต้อง
อาจจะไม่เร็วที่สุด แต่เนื่องจากประสิทธิภาพไม่ใช่ปัญหาฉันจึงพบว่ามันสวยงามและเข้าใจได้
ไชโย!
คุณสามารถทำได้ด้วยsed
คำสั่ง:
sed -e "
s/[?()\[\]=+<>:;©®”,*|]/_/g
s/"$'\t'"/ /g
s/–/-/g
s/\"/_/g
s/[[:cntrl:]]/_/g"