Maison > Java > javaDidacticiel > le corps du texte

Cas extrêmes à garder à l'esprit. Fichiers de pièces

PHPz
Libérer: 2024-08-09 07:05:22
original
390 Les gens l'ont consulté

Edge Cases to Keep in Mind. Part  Files

Saviez-vous qu'il peut y avoir un fichier qui existe et qui n'existe pas en même temps ? Savez-vous que vous pouvez supprimer un fichier tout en l'utilisant ? Découvrez ces cas extrêmes de fichiers et d'autres dans le développement logiciel.

Dans mon article précédent sur les cas extrêmes dans le développement logiciel, j'écrivais sur les pièges de texte et je vous ai donné quelques suggestions sur la façon de les éviter. Dans cet article de blog, je voudrais me concentrer sur les fichiers et les opérations d'E/S sur fichiers.

Un fichier qui n'est pas un fichier

L'API java.io.File fournit, entre autres, ces 3 méthodes :

  • #existe()

  • #isDirectory()

  • #isFile()

On peut penser que, s'il est pointé par un chemin donné qui existe, un objet est soit un fichier, soit un répertoire — comme dans cette question sur Stack Overflow. Cependant, ce n'est pas toujours vrai.

Ce n'est pas explicitement mentionné dans les javadocs File#isFile(), mais fichier **là signifie en réalité **fichier normal. Ainsi, des fichiers Unix spéciaux comme des périphériques, des sockets et des tuyaux peuvent exister mais ce ne sont pas des fichiers dans cette définition.

Regardez l'extrait suivant :

import java.io.File

val file = File("/dev/null")
println("exists:      ${file.exists()}")
println("isFile:      ${file.isFile()}")
println("isDirectory: ${file.isDirectory()}")
Copier après la connexion

Comme vous pouvez le voir sur la démo live, un fichier qui n'est ni un fichier ni un répertoire peut exister.

Exister ou ne pas exister ?

Les liens symboliques sont aussi des fichiers spéciaux mais ils sont traités de manière transparente presque partout dans l'(ancienne) API java.io. La seule exception est la famille de méthodes #getCanonicalPath()/#getCanonicalFile(). La transparence signifie ici que toutes les opérations sont transmises à la cible, tout comme elles sont effectuées directement sur elle. Une telle transparence est généralement utile, par ex. vous pouvez simplement lire ou écrire dans un fichier. Vous ne vous souciez pas de la résolution facultative du chemin de lien. Cependant, cela peut aussi conduire à des cas étranges. Par exemple, il peut y avoir un fichier qui existe et qui n'existe pas en même temps.

Considérons un lien symbolique pendant. Sa cible n'existe pas, donc toutes les méthodes de la section précédente renverront false. Néanmoins, le chemin du fichier source est toujours occupé, par ex. vous ne pouvez pas créer un nouveau fichier sur ce chemin. Voici le code illustrant ce cas :

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()}")
Copier après la connexion

Et une démo en direct.

L'ordre compte

Dans l'API java.io, pour créer un répertoire éventuellement inexistant et vous assurer qu'il existe par la suite, vous pouvez utiliser File#mkdir() (ou File#mkdirs() si vous souhaitez créer des répertoires parents inexistants comme bien) puis File#isDirectory(). Il est important d’utiliser ces méthodes dans l’ordre mentionné. Voyons ce qui pourrait arriver si l’ordre était inversé. Deux (ou plus) threads effectuant les mêmes opérations sont nécessaires pour démontrer ce cas. Ici, nous utiliserons des fils bleus et rouges.

  1. (rouge) isDirectory() ? — non, il faut créer

  2. (bleu) isDirectory() ? — non, il faut créer

  3. (rouge) mkdir() ? — succès

  4. (bleu) mkdir() ? — échouer

Comme vous pouvez le voir, un fil de discussion bleu n'a pas réussi à créer un répertoire. Cependant, il a bel et bien été créé, le résultat devrait donc être positif. Si isDirectory() avait appelé à la fin, le résultat aurait toujours été correct.

La limitation cachée

Le nombre de fichiers ouverts en même temps par un processus UNIX donné est limité à la valeur de RLIMIT_NOFILE. Sur Android, il s'agit généralement de 1 024, mais en réalité (à l'exclusion des descripteurs de fichiers utilisés par le framework), vous pouvez en utiliser encore moins (lors des tests avec une activité vide sur Android 8.0.0, il y avait environ 970 descripteurs de fichiers disponibles à utiliser). Que se passe-t-il si vous essayez d’en ouvrir davantage ? Eh bien, le fichier ne sera pas ouvert. Selon le contexte, vous pouvez rencontrer une exception avec une raison explicite (Trop de fichiers ouverts), un message un peu énigmatique (par exemple Ce fichier ne peut pas être ouvert en tant que descripteur de fichier ; il est probablement compressé) ou simplement false comme valeur de retour alors que vous vous attendez normalement à true. Voir le code illustrant ces problèmes :

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())
    }
}
Copier après la connexion

Notez que si vous utilisez #apply(), la valeur ne sera tout simplement pas enregistrée de manière persistante — vous n'obtiendrez donc aucune exception. Cependant, il sera accessible jusqu'à ce que le processus d'application contenant cette instance SharedPreferences soit supprimé. En effet, les préférences partagées sont également enregistrées dans la mémoire.

Les morts-vivants existent vraiment

On pourrait penser que les zombies, les goules et autres créatures similaires n'existent que dans les fictions fantastiques et d'horreur. Mais… ils sont réels en informatique ! Ces termes courants font référence aux processus zombies. En fait, des fichiers de morts-vivants peuvent également être facilement créés.

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()}")
}
Copier après la connexion

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.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

source:dev.to
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!