留意すべきエッジケース。パーツファイル
ファイルには存在するものと存在しないものがあることをご存知ですか?ファイルを削除してもまだ使用できることをご存知ですか?ソフトウェア開発におけるこれらおよびその他のファイルのエッジケースを発見してください。
ソフトウェア開発におけるエッジケースに関する前回の記事では、テキストトラップについて書き、それを回避する方法についていくつかの提案をしました。このブログ投稿では、ファイルとファイル I/O 操作に焦点を当てたいと思います。
ファイルではないファイル
java.io.File API は、特に次の 3 つのメソッドを提供します:
#exists()
#isDirectory()
#isFile()
Stack Overflow のこの質問のように、存在する特定のパスによってオブジェクトが指されている場合、オブジェクトはファイルまたはディレクトリのいずれかであると考える人もいるかもしれません。ただし、これが常に真実であるとは限りません。
File#isFile() Javadoc では明示的に言及されていませんが、ファイル **実際には **通常のファイルを意味します。したがって、デバイス、ソケット、パイプなどの特別な Unix ファイルが存在する可能性がありますが、それらはその定義ではファイルではありません。
次のスニペットを見てください:
import java.io.File val file = File("/dev/null") println("exists: ${file.exists()}") println("isFile: ${file.isFile()}") println("isDirectory: ${file.isDirectory()}")
ライブデモでわかるように、ファイルでもディレクトリでもないファイルが存在する可能性があります。
存在するのか、存在しないのか?
シンボリック リンクも特殊なファイルですが、(古い) java.io API ではほぼどこでも透過的に扱われます。唯一の例外は、#getCanonicalPath()/#getCanonicalFile() メソッド ファミリです。ここでの透過性とは、すべての操作が、ターゲット上で直接実行されるのと同じように、ターゲットに転送されることを意味します。このような透明性は、通常は便利です。ファイルの読み取りまたは書き込みを行うことができます。オプションのリンク パス解決は気にしません。ただし、それが奇妙なケースにつながる可能性もあります。たとえば、存在するファイルと存在しないファイルが同時に存在する可能性があります。
ぶら下がりシンボリック リンクについて考えてみましょう。そのターゲットは存在しないため、前のセクションのすべてのメソッドは false を返します。それにもかかわらず、ソース ファイル パスはまだ占有されています。そのパス上に新しいファイルを作成することはできません。このケースを示すコードは次のとおりです:
import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths val path = Paths.get("foo") Files.createSymbolicLink(path, path) println("exists : ${path.toFile().exists()}") println("isFile : ${path.toFile().isFile()}") println("isDirectory : ${path.toFile().isDirectory()}") println("createNewFile: ${path.toFile().createNewFile()}")
ライブデモもあります。
順序が重要です
java.io API では、存在しない可能性のあるディレクトリを作成し、その後それが存在することを確認するには、File#mkdir() (または、存在しない親ディレクトリを作成したい場合は File#mkdirs() を使用できます。まあ)、次に File#isDirectory() です。これらのメソッドを記載された順序で使用することが重要です。順序が逆の場合に何が起こるかを見てみましょう。このケースを実証するには、同じ操作を実行する 2 つ (またはそれ以上) のスレッドが必要です。ここでは、青と赤の糸を使用します。
(赤)はDirectory()? — いいえ、
を作成する必要があります
(青)はDirectory()? — いいえ、
を作成する必要があります
(赤) mkdir()? — 成功
(青) mkdir()? — 失敗
ご覧のとおり、青いスレッドはディレクトリの作成に失敗しました。しかし、実際に作成されたものであるため、結果はプラスになるはずです。 isDirectory() が最後に呼び出されていれば、結果は常に正しいものになっていたでしょう。
隠れた限界
特定の UNIX プロセスによって同時にオープンされるファイルの数は、RLIMIT_NOFILE の値に制限されます。 Android では、これは通常 1024 ですが、実際には (フレームワークで使用されるファイル記述子を除く)、さらに少ない数を使用できます (Android 8.0.0 で空のアクティビティを使用したテスト中、使用可能なファイル記述子は約 970 でした)。さらに開けようとするとどうなりますか?まあ、ファイルは開かれません。コンテキストによっては、明示的な理由 (開いているファイルが多すぎます) や、少し謎めいたメッセージ (例: このファイルはファイル記述子として開くことができません。おそらく圧縮されている)、または通常 true が期待されるときに戻り値として false が返されるだけです。これらの問題を示すコードを参照してください:
package pl.droidsonroids.edgetest import android.content.res.AssetFileDescriptor import android.support.test.InstrumentationRegistry import org.junit.Assert import org.junit.Test class TooManyOpenFilesTest { //asset named "test" required @Test fun tooManyOpenFilesDemo() { val context = InstrumentationRegistry.getContext() val assets = context.assets val descriptors = mutableListOf<AssetFileDescriptor>() try { for (i in 0..1024) { descriptors.add(assets.openFd("test")) } } catch (e: Exception) { e.printStackTrace() //java.io.FileNotFoundException: This file can not be opened as a file descriptor; it is probably compressed } try { context.openFileOutput("test", 0) } catch (e: Exception) { e.printStackTrace() //java.io.FileNotFoundException: /data/user/0/pl.droidsonroids.edgetest/files/test (Too many open files) } val sharedPreferences = context.getSharedPreferences("test", 0) Assert.assertTrue(sharedPreferences.edit().putBoolean("test", true).commit()) } }
#apply() を使用する場合、値は永続的に保存されないため、例外は発生しないことに注意してください。ただし、その SharedPreferences インスタンスを保持するアプリ プロセスが終了するまではアクセスできます。これは、共有設定もメモリに保存されるためです。
アンデッドは本当に存在する
ゾンビ、グール、その他の同様の生き物はファンタジーやホラー フィクションの中にのみ存在すると考える人もいるかもしれません。しかし…それらはコンピュータサイエンスにおいては現実のものなのです!このような一般的な用語は、ゾンビ プロセスを指します。実際、アンデッド ファイルも簡単に作成できます。
In Unix-like operating systems, file deletion is usually implemented by unlinking. The unlinked file name is removed from the file system (assuming that it is the last hardlink) but any already open file descriptors remain valid and usable. You can still read from and write to such a file. Here is the snippet:
import java.io.BufferedReader import java.io.File import java.io.FileReader val file = File("test") file.writeText("this is file content") BufferedReader(FileReader(file)).use { println("deleted?: ${file.delete()}") println("content?: ${it.readLine()}") }
And a live demo.
Wrap up
First of all, remember that we can’t forget about the proper method calling order when creating non-existent directories. Furthermore, keep in mind that a number of files open at the same time is limited and not only files explicitly opened by you are counted. And the last, but not least, a trick with file deletion before the last usage can give you a little bit more flexibility.
Originally published at www.thedroidsonroids.com on September 27, 2017.
以上が留意すべきエッジケース。パーツファイルの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック











一部のアプリケーションが適切に機能しないようにする会社のセキュリティソフトウェアのトラブルシューティングとソリューション。多くの企業は、内部ネットワークセキュリティを確保するためにセキュリティソフトウェアを展開します。 ...

多くのアプリケーションシナリオでソートを実装するために名前を数値に変換するソリューションでは、ユーザーはグループ、特に1つでソートする必要がある場合があります...

システムドッキングでのフィールドマッピング処理は、システムドッキングを実行する際に難しい問題に遭遇することがよくあります。システムのインターフェイスフィールドを効果的にマッピングする方法A ...

intellijideaultimatiateバージョンを使用してスプリングを開始します...

データベース操作にMyBatis-Plusまたはその他のORMフレームワークを使用する場合、エンティティクラスの属性名に基づいてクエリ条件を構築する必要があることがよくあります。あなたが毎回手動で...

Javaオブジェクトと配列の変換:リスクの詳細な議論と鋳造タイプ変換の正しい方法多くのJava初心者は、オブジェクトのアレイへの変換に遭遇します...

eコマースプラットフォーム上のSKUおよびSPUテーブルの設計の詳細な説明この記事では、eコマースプラットフォームでのSKUとSPUのデータベース設計の問題、特にユーザー定義の販売を扱う方法について説明します。

Redisキャッシュソリューションは、製品ランキングリストの要件をどのように実現しますか?開発プロセス中に、多くの場合、ランキングの要件に対処する必要があります。
