Mój program pobierze z internetu dowolne ciągi znaków i użyje ich jako nazw plików. Czy istnieje prosty sposób na usunięcie złych znaków z tych ciągów, czy też muszę napisać do tego funkcję niestandardową?
Mój program pobierze z internetu dowolne ciągi znaków i użyje ich jako nazw plików. Czy istnieje prosty sposób na usunięcie złych znaków z tych ciągów, czy też muszę napisać do tego funkcję niestandardową?
Odpowiedzi:
Ugh, nienawidzę, kiedy ludzie próbują odgadnąć, które znaki są prawidłowe. Oprócz tego, że są całkowicie nieprzenośne (zawsze myśląc o Mono), oba wcześniejsze komentarze pominęły więcej 25 nieprawidłowych znaków.
'Clean just a filename
Dim filename As String = "salmnas dlajhdla kjha;dmas'lkasn"
For Each c In IO.Path.GetInvalidFileNameChars
filename = filename.Replace(c, "")
Next
'See also IO.Path.GetInvalidPathChars
Aby usunąć nieprawidłowe znaki:
static readonly char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
// Builds a string out of valid chars
var validFilename = new string(filename.Where(ch => !invalidFileNameChars.Contains(ch)).ToArray());
Aby zamienić nieprawidłowe znaki:
static readonly char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
// Builds a string out of valid chars and an _ for invalid ones
var validFilename = new string(filename.Select(ch => invalidFileNameChars.Contains(ch) ? '_' : ch).ToArray());
Aby zamienić nieprawidłowe znaki (i uniknąć potencjalnego konfliktu nazw, takiego jak Hell * vs Hell $):
static readonly IList<char> invalidFileNameChars = Path.GetInvalidFileNameChars();
// Builds a string out of valid chars and replaces invalid chars with a unique letter (Moves the Char into the letter range of unicode, starting at "A")
var validFilename = new string(filename.Select(ch => invalidFileNameChars.Contains(ch) ? Convert.ToChar(invalidFileNameChars.IndexOf(ch) + 65) : ch).ToArray());
To pytanie było zadawane wiele razy przed i, jak wskazano wiele razy wcześniej, IO.Path.GetInvalidFileNameChars
nie jest wystarczające.
Po pierwsze, istnieje wiele nazw, takich jak PRN i CON, które są zarezerwowane i niedozwolone dla nazw plików. Istnieją inne nazwy, które nie są dozwolone tylko w folderze głównym. Nazwy kończące się kropką również są niedozwolone.
Po drugie, istnieje wiele ograniczeń długości. Przeczytaj pełną listę NTFS tutaj .
Po trzecie, możesz dołączyć do systemów plików, które mają inne ograniczenia. Na przykład nazwy plików ISO 9660 nie mogą zaczynać się od „-”, ale mogą go zawierać.
Po czwarte, co zrobisz, jeśli dwa procesy „arbitralnie” wybiorą tę samą nazwę?
Ogólnie rzecz biorąc, używanie nazw generowanych zewnętrznie dla nazw plików jest złym pomysłem. Sugeruję wygenerowanie własnych prywatnych nazw plików i wewnętrzne przechowywanie nazw czytelnych dla człowieka.
Zgadzam się z Grauenwolf i gorąco polecam Path.GetInvalidFileNameChars()
Oto mój wkład w C #:
string file = @"38?/.\}[+=n a882 a.a*/|n^%$ ad#(-))";
Array.ForEach(Path.GetInvalidFileNameChars(),
c => file = file.Replace(c.ToString(), String.Empty));
ps - to jest bardziej tajemnicze niż powinno - starałem się być zwięzły.
Array.ForEach
zamiast tylko foreach
tutaj
Path.GetInvalidFileNameChars().Aggregate(file, (current, c) => current.Replace(c, '-'))
Oto moja wersja:
static string GetSafeFileName(string name, char replace = '_') {
char[] invalids = Path.GetInvalidFileNameChars();
return new string(name.Select(c => invalids.Contains(c) ? replace : c).ToArray());
}
Nie jestem pewien, jak obliczany jest wynik GetInvalidFileNameChars, ale „Get” sugeruje, że nie jest to trywialne, więc buforuję wyniki. Co więcej, to przechodzi przez ciąg wejściowy tylko raz, a nie wiele razy, tak jak powyższe rozwiązania, które iterują zestaw nieprawidłowych znaków, zastępując je pojedynczo w ciągu źródłowym. Podoba mi się również rozwiązania oparte na Gdzie, ale wolę zastępować nieprawidłowe znaki zamiast je usuwać. Wreszcie, moja zamiana to dokładnie jeden znak, aby uniknąć konwersji znaków na ciągi podczas iteracji po ciągu.
Mówię to wszystko bez profilowania - ten po prostu „poczuł się” miło. :)
new HashSet<char>(Path.GetInvalidFileNameChars())
aby uniknąć wyliczenia O (n) - mikro-optymalizacja.
Oto funkcja, której teraz używam (dzięki jcollum za przykład w C #):
public static string MakeSafeFilename(string filename, char replaceChar)
{
foreach (char c in System.IO.Path.GetInvalidFileNameChars())
{
filename = filename.Replace(c, replaceChar);
}
return filename;
}
Dla wygody umieściłem to w klasie „Pomocnicy”.
Jeśli chcesz szybko usunąć wszystkie znaki specjalne, które są czasami bardziej czytelne dla użytkownika w nazwach plików, działa to ładnie:
string myCrazyName = "q`w^e!r@t#y$u%i^o&p*a(s)d_f-g+h=j{k}l|z:x\"c<v>b?n[m]q\\w;e'r,t.y/u";
string safeName = Regex.Replace(
myCrazyName,
"\W", /*Matches any nonword character. Equivalent to '[^A-Za-z0-9_]'*/
"",
RegexOptions.IgnoreCase);
// safeName == "qwertyuiopasd_fghjklzxcvbnmqwertyu"
\W
dopasowuje więcej niż nie-alfanumeryczne ( [^A-Za-z0-9_]
). Wszystkie „słowne” znaki Unicode (русский 中文 ... itp.) Również nie zostaną zastąpione. Ale to dobra rzecz.
.
więc musisz najpierw wyodrębnić rozszerzenie, a następnie dodać je ponownie.
static class Utils
{
public static string MakeFileSystemSafe(this string s)
{
return new string(s.Where(IsFileSystemSafe).ToArray());
}
public static bool IsFileSystemSafe(char c)
{
return !Path.GetInvalidFileNameChars().Contains(c);
}
}
Dlaczego nie przekonwertować ciągu na odpowiednik Base64 w następujący sposób:
string UnsafeFileName = "salmnas dlajhdla kjha;dmas'lkasn";
string SafeFileName = Convert.ToBase64String(Encoding.UTF8.GetBytes(UnsafeFileName));
Jeśli chcesz go przekonwertować, aby móc go przeczytać:
UnsafeFileName = Encoding.UTF8.GetString(Convert.FromBase64String(SafeFileName));
Użyłem tego do zapisania plików PNG o unikalnej nazwie z losowego opisu.
Oto, co właśnie dodałem do klasy statycznej StringExtensions ( http://github.com/Zoomicon/ClipFlair ) ClipFlair (projekt Utils.Silverlight), w oparciu o informacje zebrane z linków do powiązanych pytań dotyczących stackoverflow opublikowanych przez Dour High Arch powyżej:
public static string ReplaceInvalidFileNameChars(this string s, string replacement = "")
{
return Regex.Replace(s,
"[" + Regex.Escape(new String(System.IO.Path.GetInvalidPathChars())) + "]",
replacement, //can even use a replacement string of any length
RegexOptions.IgnoreCase);
//not using System.IO.Path.InvalidPathChars (deprecated insecure API)
}
private void textBoxFileName_KeyPress(object sender, KeyPressEventArgs e)
{
e.Handled = CheckFileNameSafeCharacters(e);
}
/// <summary>
/// This is a good function for making sure that a user who is naming a file uses proper characters
/// </summary>
/// <param name="e"></param>
/// <returns></returns>
internal static bool CheckFileNameSafeCharacters(System.Windows.Forms.KeyPressEventArgs e)
{
if (e.KeyChar.Equals(24) ||
e.KeyChar.Equals(3) ||
e.KeyChar.Equals(22) ||
e.KeyChar.Equals(26) ||
e.KeyChar.Equals(25))//Control-X, C, V, Z and Y
return false;
if (e.KeyChar.Equals('\b'))//backspace
return false;
char[] charArray = Path.GetInvalidFileNameChars();
if (charArray.Contains(e.KeyChar))
return true;//Stop the character from being entered into the control since it is non-numerical
else
return false;
}
Uważam, że używanie tego jest szybkie i łatwe do zrozumienia:
<Extension()>
Public Function MakeSafeFileName(FileName As String) As String
Return FileName.Where(Function(x) Not IO.Path.GetInvalidFileNameChars.Contains(x)).ToArray
End Function
To działa, ponieważ string
jest IEnumerable
w postaci char
tablicy i istnieje string
ciąg konstruktor, że trwa char
tablicy.
Z moich starszych projektów odnalazłem to rozwiązanie, które od ponad 2 lat działa doskonale. Zamieniam niedozwolone znaki na „!”, A następnie sprawdzam, czy nie ma podwójnych znaków !!, użyj własnego znaku.
public string GetSafeFilename(string filename)
{
string res = string.Join("!", filename.Split(Path.GetInvalidFileNameChars()));
while (res.IndexOf("!!") >= 0)
res = res.Replace("!!", "!");
return res;
}
Wiele odpowiedzi sugeruje użycie, Path.GetInvalidFileNameChars()
co wydaje mi się złym rozwiązaniem. Zachęcam do korzystania z białej listy zamiast z czarnej listy, ponieważ hakerzy zawsze znajdą sposób na obejście tego.
Oto przykład kodu, którego możesz użyć:
string whitelist = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.";
foreach (char c in filename)
{
if (!whitelist.Contains(c))
{
filename = filename.Replace(c, '-');
}
}