Tôi có một chuỗi như "Foo: Bar" mà tôi muốn sử dụng làm tên tệp, nhưng trên Windows, char ":" không được phép trong tên tệp.
Có phương pháp nào biến "Foo: Bar" thành một thứ giống như "Foo- Bar" không?
Tôi có một chuỗi như "Foo: Bar" mà tôi muốn sử dụng làm tên tệp, nhưng trên Windows, char ":" không được phép trong tên tệp.
Có phương pháp nào biến "Foo: Bar" thành một thứ giống như "Foo- Bar" không?
Câu trả lời:
Hãy thử một cái gì đó như sau:
string fileName = "something";
foreach (char c in System.IO.Path.GetInvalidFileNameChars())
{
fileName = fileName.Replace(c, '_');
}
Biên tập:
Vì GetInvalidFileNameChars()
sẽ trả về 10 hoặc 15 ký tự, tốt hơn nên sử dụng một StringBuilder
thay vì một chuỗi đơn giản; phiên bản gốc sẽ lâu hơn và tiêu tốn nhiều bộ nhớ hơn.
file.name.txt.pdf
là một pdf hợp lệ. Windows chỉ đọc .
phần mở rộng cuối cùng .
fileName = fileName.Replace(":", "-")
Tuy nhiên, ":" không phải là ký tự bất hợp pháp duy nhất cho Windows. Bạn cũng sẽ phải xử lý:
/, \, :, *, ?, ", <, > and |
Chúng được chứa trong System.IO.Path.GetInvalidFileNameChars ();
Ngoài ra (trên Windows), "." không thể là ký tự duy nhất trong tên tệp (cả ".", "..", "...", v.v. đều không hợp lệ). Hãy cẩn thận khi đặt tên tệp bằng ".", Ví dụ:
echo "test" > .test.
Sẽ tạo một tệp có tên ".test"
Cuối cùng, nếu bạn thực sự muốn thực hiện mọi thứ một cách chính xác, có một số tên tệp đặc biệt mà bạn cần chú ý. Trên Windows, bạn không thể tạo tệp có tên:
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.
Điều này không hiệu quả hơn, nhưng nó thú vị hơn :)
var fileName = "foo:bar";
var invalidChars = System.IO.Path.GetInvalidFileNameChars();
var cleanFileName = new string(fileName.Where(m => !invalidChars.Contains(m)).ToArray<char>());
Trong trường hợp bất kỳ ai muốn một phiên bản được tối ưu hóa dựa trên StringBuilder
, hãy sử dụng cái này. Bao gồm thủ thuật của rkagerer như một tùy chọn.
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;
}
Đây là phiên bản của câu trả lời được chấp nhận sử Linq
dụng Enumerable.Aggregate
:
string fileName = "something";
Path.GetInvalidFileNameChars()
.Aggregate(fileName, (current, c) => current.Replace(c, '_'));
Diego có giải pháp chính xác nhưng có một sai lầm rất nhỏ trong đó. Phiên bản của string.Replace đang được sử dụng phải là string.Replace (char, char), không có string.Replace (char, string)
Tôi không thể chỉnh sửa câu trả lời hoặc tôi chỉ thực hiện một thay đổi nhỏ.
Nên nó phải là:
string fileName = "something";
foreach (char c in System.IO.Path.GetInvalidFileNameChars())
{
fileName = fileName.Replace(c, '_');
}
Đây là một chút thay đổi về câu trả lời của Diego.
Nếu bạn không sợ Unicode, bạn có thể giữ lại độ trung thực hơn một chút bằng cách thay thế các ký tự không hợp lệ bằng các ký hiệu Unicode hợp lệ giống với chúng. Đây là mã tôi đã sử dụng trong một dự án gần đây liên quan đến danh sách gỗ xẻ:
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;
}
Điều này tạo ra các tên tệp như 1⁄2” spruce.txt
thay vì1_2_ spruce.txt
Có, nó thực sự hoạt động:
Emptor caveat
Tôi biết thủ thuật này sẽ hoạt động trên NTFS nhưng rất ngạc nhiên khi thấy nó cũng hoạt động trên các phân vùng FAT và FAT32. Đó là bởi vì các tên tệp dài được lưu trữ bằng Unicode , ngay cả khi Windows 95 / NT. Tôi đã thử nghiệm trên Win7, XP và thậm chí cả một bộ định tuyến dựa trên Linux và chúng đều hiển thị OK. Không thể nói như vậy đối với bên trong DOSBox.
Điều đó nói rằng, trước khi bắt đầu làm việc này, hãy cân nhắc xem liệu bạn có thực sự cần thêm sự chung thủy hay không. Các bí danh Unicode có thể gây nhầm lẫn cho mọi người hoặc các chương trình cũ, ví dụ như hệ điều hành cũ dựa vào các mã .
Đây là phiên bản sử dụng StringBuilder
và IndexOfAny
với phần phụ thêm hàng loạt để đạt hiệu quả đầy đủ. Nó cũng trả về chuỗi gốc thay vì tạo một chuỗi trùng lặp.
Cuối cùng nhưng không kém phần quan trọng, nó có một câu lệnh chuyển đổi trả về các ký tự trông giống nhau mà bạn có thể tùy chỉnh theo bất kỳ cách nào bạn muốn. Kiểm tra tra cứu về sự nhầm lẫn của Unicode.org để xem bạn có thể có những tùy chọn nào, tùy thuộc vào phông chữ.
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();
}
Nó không kiểm tra .
, ..
hoặc tên reserved thíchCON
vì nó không được rõ ràng những gì mà thay thế nên.
Đang làm sạch một chút mã của tôi và tái cấu trúc một chút ... Tôi đã tạo một tiện ích mở rộng cho loại chuỗi:
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));
}
Giờ đây, nó dễ sử dụng hơn với:
var name = "Any string you want using ? / \ or even +.zip";
var validFileName = name.ToValidFileName();
Nếu bạn muốn thay thế bằng một ký tự khác với "_", bạn có thể sử dụng:
var validFileName = name.ToValidFileName(replaceChar:'#');
Và bạn có thể thêm ký tự để thay thế .. ví dụ như bạn không muốn dấu cách hoặc dấu phẩy:
var validFileName = name.ToValidFileName(includeChars: new [] { ' ', ',' });
Hy vọng nó giúp...
Chúc mừng
Một giải pháp đơn giản khác:
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());
}
Một mã một dòng đơn giản:
var validFileName = Path.GetInvalidFileNameChars().Aggregate(fileName, (f, c) => f.Replace(c, '_'));
Bạn có thể bọc nó trong một phương thức mở rộng nếu bạn muốn sử dụng lại nó.
public static string ToValidFileName(this string fileName) => Path.GetInvalidFileNameChars().Aggregate(fileName, (f, c) => f.Replace(c, '_'));
Tôi cần một hệ thống không thể tạo ra va chạm vì vậy tôi không thể ánh xạ nhiều ký tự thành một. Tôi đã kết thúc với:
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)));
}
}
Tôi cần làm điều này hôm nay ... trong trường hợp của tôi, tôi cần nối tên khách hàng với ngày và giờ cho tệp .kmz cuối cùng. Giải pháp cuối cùng của tôi là:
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));
Bạn thậm chí có thể làm cho nó thay thế khoảng trắng nếu bạn thêm ký tự khoảng trắng vào mảng không hợp lệ.
Có thể nó không phải là nhanh nhất, nhưng vì hiệu suất không phải là một vấn đề, tôi thấy nó thanh lịch và dễ hiểu.
Chúc mừng!
Bạn có thể thực hiện việc này bằng sed
lệnh:
sed -e "
s/[?()\[\]=+<>:;©®”,*|]/_/g
s/"$'\t'"/ /g
s/–/-/g
s/\"/_/g
s/[[:cntrl:]]/_/g"