Ich habe eine Zeichenfolge wie "Foo: Bar", die ich als Dateinamen verwenden möchte, aber unter Windows ist das Zeichen ":" in einem Dateinamen nicht zulässig.
Gibt es eine Methode, die "Foo: Bar" in so etwas wie "Foo-Bar" verwandelt?
Ich habe eine Zeichenfolge wie "Foo: Bar", die ich als Dateinamen verwenden möchte, aber unter Windows ist das Zeichen ":" in einem Dateinamen nicht zulässig.
Gibt es eine Methode, die "Foo: Bar" in so etwas wie "Foo-Bar" verwandelt?
Antworten:
Versuchen Sie so etwas:
string fileName = "something";
foreach (char c in System.IO.Path.GetInvalidFileNameChars())
{
fileName = fileName.Replace(c, '_');
}
Bearbeiten:
Da GetInvalidFileNameChars()
10 oder 15 Zeichen zurückgegeben werden, ist es besser, eine StringBuilder
anstelle einer einfachen Zeichenfolge zu verwenden. Die Originalversion dauert länger und verbraucht mehr Speicher.
file.name.txt.pdf
ist ein gültiges PDF. Windows liest nur den letzten .
für die Erweiterung.
fileName = fileName.Replace(":", "-")
":" Ist jedoch nicht das einzige unzulässige Zeichen für Windows. Sie müssen auch behandeln:
/, \, :, *, ?, ", <, > and |
Diese sind in System.IO.Path.GetInvalidFileNameChars () enthalten.
Auch (unter Windows) "." kann nicht das einzige Zeichen im Dateinamen sein (beide ".", "..", "..." usw. sind ungültig). Seien Sie vorsichtig, wenn Sie Dateien mit "." Benennen, zum Beispiel:
echo "test" > .test.
Generiert eine Datei mit dem Namen ".test"
Wenn Sie die Dinge wirklich richtig machen möchten, müssen Sie einige spezielle Dateinamen beachten . Unter Windows können Sie keine Dateien mit folgenden Namen erstellen:
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.
Das ist nicht effizienter, aber es macht mehr Spaß :)
var fileName = "foo:bar";
var invalidChars = System.IO.Path.GetInvalidFileNameChars();
var cleanFileName = new string(fileName.Where(m => !invalidChars.Contains(m)).ToArray<char>());
Wenn jemand eine optimierte Version basierend auf möchte StringBuilder
, verwenden Sie diese. Beinhaltet optional den Trick von 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;
}
Hier ist eine Version der akzeptierten Antwort, Linq
die Folgendes verwendet Enumerable.Aggregate
:
string fileName = "something";
Path.GetInvalidFileNameChars()
.Aggregate(fileName, (current, c) => current.Replace(c, '_'));
Diego hat zwar die richtige Lösung, aber es gibt einen sehr kleinen Fehler. Die verwendete Version von string.Replace sollte string.Replace (char, char) sein, es gibt keinen string.Replace (char, string)
Ich kann die Antwort nicht bearbeiten, sonst hätte ich nur die geringfügige Änderung vorgenommen.
So sollte es sein:
string fileName = "something";
foreach (char c in System.IO.Path.GetInvalidFileNameChars())
{
fileName = fileName.Replace(c, '_');
}
Hier ist eine kleine Wendung zu Diego's Antwort.
Wenn Sie keine Angst vor Unicode haben, können Sie die Wiedergabetreue verbessern, indem Sie die ungültigen Zeichen durch gültige Unicode-Symbole ersetzen, die ihnen ähneln. Hier ist der Code, den ich kürzlich in einem Projekt mit Schnittholz-Schnittlisten verwendet habe:
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;
}
Dies erzeugt Dateinamen wie 1⁄2” spruce.txt
anstelle von1_2_ spruce.txt
Ja, es funktioniert wirklich:
Vorbehalt Emptor
Ich wusste, dass dieser Trick unter NTFS funktionieren würde, war aber überrascht, dass er auch auf FAT- und FAT32-Partitionen funktioniert. Dies liegt daran, dass lange Dateinamen in Unicode gespeichert sind , sogar schon unter Windows 95 / NT. Ich habe auf Win7, XP und sogar einem Linux-basierten Router getestet und sie haben sich als OK erwiesen. Kann nicht dasselbe für innerhalb einer DOSBox sagen.
Bevor Sie jedoch verrückt werden, sollten Sie überlegen, ob Sie wirklich die zusätzliche Wiedergabetreue benötigen. Die Unicode-Look-Alikes können Personen oder alte Programme verwirren, z. B. ältere Betriebssysteme, die auf Codepages angewiesen sind .
Hier ist eine Version, die verwendet StringBuilder
und IndexOfAny
mit Bulk-Append für volle Effizienz. Es wird auch die ursprüngliche Zeichenfolge zurückgegeben, anstatt eine doppelte Zeichenfolge zu erstellen.
Last but not least gibt es eine switch-Anweisung, die ähnliche Zeichen zurückgibt, die Sie nach Belieben anpassen können. Schauen Sie sich die verwirrbare Suche von Unicode.org an, um zu sehen, welche Optionen Sie je nach Schriftart haben könnten.
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();
}
Es sucht nicht nach .
, ..
oder reservierte Namen wie , CON
weil es nicht klar ist , was sollte der Ersatz sein.
Ein wenig meinen Code bereinigen und ein wenig umgestalten ... Ich habe eine Erweiterung für den String-Typ erstellt:
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));
}
Jetzt ist es einfacher zu verwenden mit:
var name = "Any string you want using ? / \ or even +.zip";
var validFileName = name.ToValidFileName();
Wenn Sie durch ein anderes Zeichen als "_" ersetzen möchten, können Sie Folgendes verwenden:
var validFileName = name.ToValidFileName(replaceChar:'#');
Und Sie können Zeichen hinzufügen, um sie zu ersetzen. Zum Beispiel möchten Sie keine Leerzeichen oder Kommas:
var validFileName = name.ToValidFileName(includeChars: new [] { ' ', ',' });
Ich hoffe es hilft...
Prost
Eine weitere einfache Lösung:
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());
}
Ein einfacher einzeiliger Code:
var validFileName = Path.GetInvalidFileNameChars().Aggregate(fileName, (f, c) => f.Replace(c, '_'));
Sie können es in eine Erweiterungsmethode einschließen, wenn Sie es wiederverwenden möchten.
public static string ToValidFileName(this string fileName) => Path.GetInvalidFileNameChars().Aggregate(fileName, (f, c) => f.Replace(c, '_'));
Ich brauchte ein System, das keine Kollisionen erzeugen konnte, sodass ich nicht mehrere Zeichen einem zuordnen konnte. Am Ende hatte ich:
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)));
}
}
Ich musste dies heute tun ... in meinem Fall musste ich einen Kundennamen mit dem Datum und der Uhrzeit für eine endgültige .kmz-Datei verketten. Meine endgültige Lösung war folgende:
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));
Sie können sogar Leerzeichen ersetzen, wenn Sie das Leerzeichen char zum ungültigen Array hinzufügen.
Vielleicht ist es nicht das schnellste, aber da Leistung kein Problem war, fand ich es elegant und verständlich.
Prost!
Sie können dies mit einem sed
Befehl tun :
sed -e "
s/[?()\[\]=+<>:;©®”,*|]/_/g
s/"$'\t'"/ /g
s/–/-/g
s/\"/_/g
s/[[:cntrl:]]/_/g"