Javaに、ファイルを作成せずにパスが有効かどうかを判断する方法はありますか?
ユーザーが指定した文字列が有効なファイルパスであるかどうかを判断する必要があります(つまり、createNewFile()
が成功するか、例外をスローする場合)検証のため。
ファイルを作成せずに、私が持っている文字列が有効なファイルパスかどうかを判断する方法はありますか?
「有効なファイルパス」の定義はOSによって異なることは知っていますが、C:/foo
または/foo
およびbanana
を拒否します。
可能なアプローチは、ファイルを作成しようとし、作成が成功した場合に最終的に削除することですが、同じ結果を達成するよりエレガントな方法があることを望みます。
これにより、ディレクトリの存在もチェックされます。
File file = new File("c:\\cygwin\\cygwin.bat");
if (!file.isDirectory())
file = file.getParentFile();
if (file.exists()){
...
}
ディレクトリへの書き込み権限がある場合、file.canWrite()は明確な指示を与えないようです。
Java 7で導入されたパスクラスは、次のような新しい選択肢を追加します。
(Linuxの下でnotが正常に動作します-常にtrueを返します)
/**
* <pre>
* Checks if a string is a valid path.
* Null safe.
*
* Calling examples:
* isValidPath("c:/test"); //returns true
* isValidPath("c:/te:t"); //returns false
* isValidPath("c:/te?t"); //returns false
* isValidPath("c/te*t"); //returns false
* isValidPath("good.txt"); //returns true
* isValidPath("not|good.txt"); //returns false
* isValidPath("not:good.txt"); //returns false
* </pre>
*/
public static boolean isValidPath(String path) {
try {
Paths.get(path);
} catch (InvalidPathException | NullPointerException ex) {
return false;
}
return true;
}
File.getCanonicalPath()
は、この目的には非常に便利です。 IO特定の種類の無効なファイル名に対して例外がスローされます(例:CON
、PRN
、*?*
(Windowsの場合)OSまたはファイルシステムに対して解決する場合。ただし、これは予備チェックとしてのみ機能します。実際にファイルを作成するときに他のエラー(たとえば、アクセス許可の不足、ドライブ領域の不足、セキュリティ制限など)を処理する必要があります。
ファイルを作成しようとすると、いくつかの問題が発生する可能性があります。
- 必要な権限がありません。
- デバイスに十分なスペースがありません。
- デバイスでエラーが発生しました。
- カスタムセキュリティのポリシーによっては、特定の種類のファイルを作成できません。
- 等.
さらに言えば、それらは、あなたができるかどうか、実際にできるかどうかを確認するために、クエリを試みるときとクエリを変えるときに変えることができます。マルチスレッド環境では、これは競合状態の主な原因の1つであり、一部のプログラムの本当の脆弱性になる可能性があります。
基本的には、試してみて作成し、機能するかどうかを確認するだけです。それが正しい方法です。 ConcurrentHashMap
のようなものにはputIfAbsent()
があるので、チェックと挿入はアトミック操作であり、競合状態の影響を受けません。まったく同じ原則がここで機能しています。
これが診断またはインストールプロセスの一部にすぎない場合は、実行して動作するかどうかを確認してください。繰り返しますが、後で動作するという保証はありません。
基本的に、プログラムは、関連するファイルを書き込めない場合に正常に終了するのに十分な堅牢性を備えている必要があります。
これは、オペレーティングシステム間で機能することができます
正規表現の一致を使用して、既存の無効な文字を確認します。
if (newName.matches(".*[/\n\r\t\0\f`?*\\<>|\":].*")) {
System.out.println("Invalid!");
} else {
System.out.println("Valid!");
}
長所
- これは複数のオペレーティングシステムで機能します
- その正規表現を編集することで、好きなようにカスタマイズできます。
短所
- これは完全なリストではない可能性があり、より多くの無効なパターンまたは文字を埋めるためにさらに調査する必要があります。
ただそれをしてください
可能なアプローチは、ファイルを作成しようとし、作成が成功した場合に最終的に削除することですが、同じ結果を達成するよりエレガントな方法があることを望みます。
たぶんそれが最も堅牢な方法でしょう。
以下は、プログラムがファイルを作成できるかどうかを決定するcanCreateOrIsWritable
ですおよびその親ディレクトリ指定されたパスで、または既にファイルが存在する場合は書き込みます。
これは、必要な親ディレクトリとパスに空のファイルを実際に作成することにより行われます。その後、それらを削除します(パスにファイルが存在する場合は、そのまま残されます)。
使用方法は次のとおりです。
var myFile = new File("/home/me/maybe/write/here.log")
if (canCreateOrIsWritable(myFile)) {
// We're good. Create the file or append to it
createParents(myFile);
appendOrCreate(myFile, "new content");
} else {
// Let's pick another destination. Maybe the OS's temporary directory:
var tempDir = System.getProperty("Java.io.tmpdir");
var alternative = Paths.get(tempDir, "second_choice.log");
appendOrCreate(alternative, "new content in temporary directory");
}
いくつかのヘルパーメソッドを備えた必須メソッド:
static boolean canCreateOrIsWritable(File file) {
boolean canCreateOrIsWritable;
// The non-existent ancestor directories of the file.
// The file's parent directory is first
List<File> parentDirsToCreate = getParentDirsToCreate(file);
// Create the parent directories that don't exist, starting with the one
// highest up in the file system hierarchy (closest to root, farthest
// away from the file)
reverse(parentDirsToCreate).forEach(File::mkdir);
try {
boolean wasCreated = file.createNewFile();
if (wasCreated) {
canCreateOrIsWritable = true;
// Remove the file and its parent dirs that didn't exist before
file.delete();
parentDirsToCreate.forEach(File::delete);
} else {
// There was already a file at the path → Let's see if we can
// write to it
canCreateOrIsWritable = Java.nio.file.Files.isWritable(file.toPath());
}
} catch (IOException e) {
// File creation failed
canCreateOrIsWritable = false;
}
return canCreateOrIsWritable;
}
static List<File> getParentDirsToCreate(File file) {
var parentsToCreate = new ArrayList<File>();
File parent = file.getParentFile();
while (parent != null && !parent.exists()) {
parentsToCreate.add(parent);
parent = parent.getParentFile();
}
return parentsToCreate;
}
static <T> List<T> reverse(List<T> input) {
var reversed = new ArrayList<T>();
for (int i = input.size() - 1; i >= 0; i--) {
reversed.add(input.get(i));
}
return reversed;
}
static void createParents(File file) {
File parent = file.getParentFile();
if (parent != null) {
parent.mkdirs();
}
}
canCreateOrIsWritable
を呼び出してから実際のファイルを作成するまでの間に、ファイルシステムの内容と権限が変更された可能性があることに注意してください。