web-dev-qa-db-ja.com

Windowsでファイル名を検証する

public static boolean isValidName(String text)
{
    Pattern pattern = Pattern.compile("^[^/./\\:*?\"<>|]+$");
    Matcher matcher = pattern.matcher(text);
    boolean isMatch = matcher.matches();
    return isMatch;
}

この方法は、Windowsで有効なファイル名を保証しますか?

55
Eng.Fouad

以前に 引用されたMSDNドキュメント で指定された要件を考えると、次の正規表現はかなり良い仕事をするはずです:

public static boolean isValidName(String text)
{
    Pattern pattern = Pattern.compile(
        "# Match a valid Windows filename (unspecified file system).          \n" +
        "^                                # Anchor to start of string.        \n" +
        "(?!                              # Assert filename is not: CON, PRN, \n" +
        "  (?:                            # AUX, NUL, COM1, COM2, COM3, COM4, \n" +
        "    CON|PRN|AUX|NUL|             # COM5, COM6, COM7, COM8, COM9,     \n" +
        "    COM[1-9]|LPT[1-9]            # LPT1, LPT2, LPT3, LPT4, LPT5,     \n" +
        "  )                              # LPT6, LPT7, LPT8, and LPT9...     \n" +
        "  (?:\\.[^.]*)?                  # followed by optional extension    \n" +
        "  $                              # and end of string                 \n" +
        ")                                # End negative lookahead assertion. \n" +
        "[^<>:\"/\\\\|?*\\x00-\\x1F]*     # Zero or more valid filename chars.\n" +
        "[^<>:\"/\\\\|?*\\x00-\\x1F\\ .]  # Last char is not a space or dot.  \n" +
        "$                                # Anchor to end of string.            ", 
        Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE | Pattern.COMMENTS);
    Matcher matcher = pattern.matcher(text);
    boolean isMatch = matcher.matches();
    return isMatch;
}

この正規表現では、ファイル名の長さに制限はありませんが、実際のファイル名はプラットフォームに応じて260文字または32767文字に制限される場合があります。

88
ridgerunner

十分ではありません。WindowsとDOSでは、一部の単語は予約されている場合があり、ファイル名として使用できません。

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.

参照〜

http://en.wikipedia.org/wiki/Filename


編集:

Windowsは通常、ファイル名を260文字に制限します。しかし、完全なパス(C:\ Program Files\filename.txtなど)がこの文字カウントに含まれているため、ファイル名は実際にはそれより短くなければなりません。

これが、非常に長いファイル名のファイルを現在の場所よりも長いパスを持つ場所にコピーするときに、時々エラーが発生する理由です。

26
Monday

さて、次の方法で有効なファイル名が保証されると思います。

public static boolean isValidName(String text)
{
    try
    {
        File file = new File(text);
        file.createNewFile();
        if(file.exists()) file.delete();
        return true;
    }
    catch(Exception ex){}
    return false;
}

どう思いますか?

16
Eng.Fouad

一般に、Windowsファイル名が有効であること、つまりその名前のファイルを作成することが合法であることを保証する方法を実装することは不可能です。

Windowsファイル名がinvalidであることを保証するのは比較的簡単です。他の正規表現のいくつかはこれを試みます。ただし、元の質問はより強力なアサーションを要求します。ファイル名がguaranteesWindowsで有効な方法です。

MSDNリファレンス は、Windowsファイル名に「ターゲットファイルシステムが許可しないその他の文字」を含めることができないことを示しています。たとえば、NULを含むファイルは一部のファイルシステムでは無効になり、一部の古いファイルシステムではUnicode文字が拡張されます。したがって、☃.txtというファイルは有効な場合もありますが、そうでない場合もあります。したがって、架空のisValidName(\"☃\")がtrueを返すかどうかは、基礎となるファイルシステムに依存します。

ただし、このような関数は保守的であり、印刷可能なASCII文字。任意のファイルシステム用のドライバをインストールでき、たとえば「n」という文字を許可しないファイルシステムを自由に作成できます。したがって、「snowman.txt」のような単純なファイルでさえ「保証」することはできません。有効である.

しかし、極端な場合は別としても、他の合併症があります。たとえば、「$ LogFile」という名前のファイルはNTFSボリュームのルートには存在できませんが、ボリューム上の他の場所に存在できます。したがって、ディレクトリを知らないと、「$ LogFile」が有効な名前であるかどうかを知ることができません。ただし、「C:\ data \」が別のNTFSボリュームルートへのシンボリックリンクである場合、「C:\ data\$ LogFile」でさえ無効になる可能性があります。 (同様に、D:がNTFSボリュームのサブディレクトリのエイリアスである場合、「D:\ $ LogFile」は有効です。)

さらに複雑な問題があります。たとえば、ファイルの代替データストリームはNTFSボリュームで有効であるため、「snowman.txt:☃」が有効な場合があります。 3つの主要なWindowsファイルシステムすべてにパスの長さの制限があるため、ファイル名の有効性はパスの関数でもあります。ただし、パスがボリューム上の物理パスではなく仮想エイリアス、マップされたネットワークドライブ、またはシンボリックリンクである場合、物理パスの長さはisValidNameで使用できない場合があります。

提案された名前でファイルを作成し、それを削除し、作成が成功した場合にのみtrueを返します。このアプローチには、いくつかの実用的および理論的な問題があります。前述のとおり、有効性はファイル名とパスの両方の関数であるため、c:\ test \☃.txtの有効性はc:\ test2 \☃.txtの有効性と異なる場合があります。また、関数は、ディレクトリへの書き込み権限がないなど、ファイルの有効性に関係しないさまざまな理由でファイルの書き込みに失敗します。 3番目の欠陥は、ファイル名の有効性が非決定的である必要がないことです:仮のファイルシステムは、たとえば、削除されたファイルの置き換えを許可しないか、(理論上)ファイル名が有効かどうかをランダムに決定することさえできます。

別の方法として、ファイルがWindowsでnotであることが保証されている場合にtrueを返すメソッドisInvalidFileName(String text)を作成するのはかなり簡単です; 「aux」、「*」、「abc.txt」などのファイル名trueを返します。ファイル作成操作では、最初にファイル名が無効であることが保証されていることを確認し、falseが返された場合は停止します。そうしないと、ファイル名が無効であるためにファイルを作成できないEdgeの場合に備えて、メソッドはファイルを作成しようとする可能性があります。

13
drf

Eng.Fouadのコードにコメントするための担当者のしきい値がないため、新しい回答を投稿する

public static boolean isValidName(String text)
{
    try
    {
        File file = new File(text);
        if(file.createNewFile()) file.delete();
        return true;
    }
    catch(Exception ex){}
    return false;
}

既存のファイルを削除できないようにする回答への小さな変更。ファイルは、このメソッドの呼び出し中に作成された場合にのみ削除されますが、戻り値は同じです。

9
Abdul Hfuda

ここ 許可されているファイル名を見つけることができます。

次の文字は使用できません。

  • <(より小さい)
  • (より大きい)

  • :(コロン)
  • "(二重引用符)
  • /(スラッシュ)
  • \(バックスラッシュ)
  • | (垂直バーまたはパイプ)
  • ? (疑問符)
  • *(アスタリスク)

  • 整数値ゼロ。ASCII NUL文字と呼ばれることもあります。

  • これらの文字が許可される代替データストリームを除き、整数表現が1〜31の範囲にある文字。ファイルストリームの詳細については、「ファイルストリーム」を参照してください。
  • ターゲットファイルシステムが許可しないその他の文字。
7
phimuemue

いいね。少なくともこのリソースを信じている場合: http://msdn.Microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx

しかし、コードの使用を簡素化します。名前が無効であると言うには、これらの文字の1つを探すだけで十分です。

public static boolean isValidName(String text)
{
    Pattern pattern = Pattern.compile("[^/./\\:*?\"<>|]");
    return !pattern.matcher(text).find();
}

この正規表現はよりシンプルで、より高速に動作します。

6
AlexR

このソリューションは、ファイルを作成せずに、指定されたファイル名がOSルールに従って有効かどうかのみをチェックします。

実際にファイルを作成するときは、他の障害(たとえば、アクセス許可の不足、ドライブ領域の不足、セキュリティ制限など)を処理する必要があります。

import Java.io.File;
import Java.io.IOException;

public class FileUtils {
  public static boolean isFilenameValid(String file) {
    File f = new File(file);
    try {
       f.getCanonicalPath();
       return true;
    }
    catch (IOException e) {
       return false;
    }
  }

  public static void main(String args[]) throws Exception {
    // true
    System.out.println(FileUtils.isFilenameValid("well.txt"));
    System.out.println(FileUtils.isFilenameValid("well well.txt"));
    System.out.println(FileUtils.isFilenameValid(""));

    //false
    System.out.println(FileUtils.isFilenameValid("test.T*T"));
    System.out.println(FileUtils.isFilenameValid("test|.TXT"));
    System.out.println(FileUtils.isFilenameValid("te?st.TXT"));
    System.out.println(FileUtils.isFilenameValid("con.TXT")); // windows
    System.out.println(FileUtils.isFilenameValid("prn.TXT")); // windows
    }
  }
6
RealHowTo

Java(正規表現または独自のメソッドのいずれか)での実装方法がわかりません。ただし、Windows OSには、ファイルシステムにファイル/ディレクトリを作成するための次のルールがあります。

  1. 名前はドットだけではありません
  2. AUX、CON、NUL、PRN、COM1、COM2、COM3、COM4、COM5、COM6、COM7、COM8、COM9、LPT1、LPT2、LPT3、LPT4、LPT5、LPT6、LPT7、LPT8、LPT9などのWindowsデバイス名は使用できませんファイル名にもファイル名の最初のセグメントにも使用されます(すなわち、test1.txtのtest1)。
  3. デバイス名は大文字と小文字が区別されません。 (つまり、prn、PRN、Prnなどは同一です。)
  4. ASCII 31よりも大きいすべての文字は、「* /:<>?\ |

そのため、プログラムはこれらの規則に従う必要があります。質問の検証規則が網羅されていることを願っています。

2
Ganesan

すべての予約名(AUX、CONなど)を確認してから、次のコードを使用できます。

bool invalidName = GetFileAttributes(name) == INVALID_FILE_ATTRIBUTES && 
        GetLastError() == ERROR_INVALID_NAME;

追加の制限を確認します。ただし、存在しないディレクトリで名前を確認すると、その名前が本当に有効かどうかに関係なくERROR_PATH_NOT_FOUNDを取得することに注意してください。

とにかく、古いことわざを覚えておく必要があります。

許可を得るよりも許しを求める方が簡単です。

1
rodrigo