web-dev-qa-db-ja.com

C#で文字列をファイルパスセーフにする方法はありますか?

私のプログラムは、インターネットから任意の文字列を取得し、ファイル名に使用します。これらの文字列から不良文字を削除する簡単な方法はありますか、またはこのためのカスタム関数を作成する必要がありますか?

83
Martin Doms

うーん、人々がどのキャラクターが有効かを推測しようとするとき、私はそれを嫌います。完全に移植性がないことに加えて(常にMonoについて考えている)、以前のコメントは両方とも25個以上の無効な文字を見逃していました。

'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
161
Jonathan Allen

この質問は 多くtimesbefore で、何度も指摘されているように、IO.Path.GetInvalidFileNameCharsでは不十分です。

最初に、PRNやCONのような多くの名前があり、ファイル名には予約されていません。ルートフォルダーでのみ許可されていない名前もあります。ピリオドで終わる名前も許可されません。 。

第二に、長さにはさまざまな制限があります。 NTFSの完全なリストを読む here

第三に、他の制限があるファイルシステムに接続できます。たとえば、ISO 9660ファイル名は「-」で始めることはできませんが、含めることができます。

第4に、2つのプロセスが「任意に」同じ名前を選択した場合はどうしますか?

一般に、ファイル名に外部で生成された名前を使用するのは悪い考えです。独自のプライベートファイル名を生成し、人間が読める名前を内部に保存することをお勧めします。

31
Dour High Arch

無効な文字を削除するには:

static readonly char[] invalidFileNameChars = Path.GetInvalidFileNameChars();

// Builds a string out of valid chars
var validFilename = new string(filename.Where(ch => !invalidFileNameChars.Contains(ch)).ToArray());

無効な文字を置き換えるには:

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

無効な文字を置き換える(およびHell *と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());
31
Squirrel

私はGrauenwolfに同意し、Path.GetInvalidFileNameChars()を強くお勧めします

これが私のC#の貢献です。

string file = @"38?/.\}[+=n a882 a.a*/|n^%$ ad#(-))";
Array.ForEach(Path.GetInvalidFileNameChars(), 
      c => file = file.Replace(c.ToString(), String.Empty));

追伸-これは本来よりも謎めいた-簡潔にしようとしていた。

21
Aaron Wagner

これが私のバージョンです。

static string GetSafeFileName(string name, char replace = '_') {
  char[] invalids = Path.GetInvalidFileNameChars();
  return new string(name.Select(c => invalids.Contains(c) ? replace : c).ToArray());
}

GetInvalidFileNameCharsの結果がどのように計算されるかはわかりませんが、「Get」はそれが自明でないことを示唆しているので、結果をキャッシュします。さらに、これは、無効な文字のセットを反復処理し、ソース文字列で一度に1つずつ置き換える上記のソリューションのように、入力文字列を複数回ではなく1回だけトラバースします。また、Whereベースのソリューションも気に入っていますが、無効な文字を削除する代わりに置き換えることを好みます。最後に、文字列を反復処理するときに文字が文字列に変換されるのを避けるため、私の置換は正確に1文字です。

私は、プロファイリングを行わないすべてのことを言います-これはただ私に「感じた」だけです。 :)

12
csells

私が今使用している関数は次のとおりです(C#の例についてはjcollumに感謝します)。

public static string MakeSafeFilename(string filename, char replaceChar)
{
    foreach (char c in System.IO.Path.GetInvalidFileNameChars())
    {
        filename = filename.Replace(c, replaceChar);
    }
    return filename;
}

便宜上、これを「ヘルパー」クラスに入れました。

10
sidewinderguy

ファイル名が読みやすい場合があるすべての特殊文字をすばやく削除したい場合、これはうまく機能します。

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"
6
Keith
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);
    }
}
5
Ronnie Overby

これは、ClipFlairの( http://github.com/Zoomicon/ClipFlair )に追加したものです。StringExtensions静的クラス(Utils.Silverlightプロジェクト)。上記のDour High Arch:

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)
}
5
George Birbilis

文字列を次のようなBase64に変換しないのはなぜですか。

string UnsafeFileName = "salmnas dlajhdla kjha;dmas'lkasn";
string SafeFileName = Convert.ToBase64String(Encoding.UTF8.GetBytes(UnsafeFileName));

読み直せるように変換したい場合:

UnsafeFileName = Encoding.UTF8.GetString(Convert.FromBase64String(SafeFileName));

これを使用して、PNGファイルをランダムな説明から一意の名前で保存しました。

4
Bart Vanseer
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;            
}
2
ecklerpa

私はこれを使って素早く簡単に理解できると思います:

<Extension()>
Public Function MakeSafeFileName(FileName As String) As String
    Return FileName.Where(Function(x) Not IO.Path.GetInvalidFileNameChars.Contains(x)).ToArray
End Function

これは、stringIEnumerable配列としてcharであり、string配列を取るcharコンストラクター文字列があるために機能します。

1
cjbarth

多くの回答者はPath.GetInvalidFileNameChars()を使用することを提案していますが、これは私にとっては悪い解決策のようです。ハッカーは最終的にそれをバイパスする方法を常に見つけるので、ブラックリストの代わりにホワイトリストを使用することをお勧めします。

使用できるコードの例を次に示します。

    string whitelist = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.";
    foreach (char c in filename)
    {
        if (!whitelist.Contains(c))
        {
            filename = filename.Replace(c, '-');
        }
    }
0
AnonBird