要记住的边缘情况零件文件
2024-08-14 11:11:10
你知道吗,可能同时存在或不存在一个文件吗?你知道,你可以删除文件并仍然使用它吗?在软件开发中发现这些和其他文件的边缘。
在我之前关于软件开发边缘的文章中,我写了一篇关于文本陷阱的文章,并给了你一些建议,以及如何避免它们。在这篇博客文章中,我想关注文件和文件 i/o 操作。
不是文件的文件java.io.file api 提供了以下 3 种方法:
#exists()
#isdirectory()
#isfile()
人们可能会认为,如果它指向存在的给定路径,对象要么是文件,要么是目录 - 就像 stack overflow 这个问题是一样的。然而,这并不总是正确的。
file#isfile() javadocs 文件中没有明确提及,但文件中没有明确提及 **确实意味着 **常规文件。因此,特殊的 unix 可能存在文件(如设备、套接字和管道),但它们不是定义中的文件。
看下面的片段:
import java.io.file val file = file("/dev/null") println("exists: ${file.exists()}") println("isfile: ${file.isfile()}") println("isdirectory: ${file.isdirectory()}")
就像你在现场演示中看到的,可能既不是文件也不是目录。 file。
存在还是不存在?符号链接也是一个特殊的文件,但在(旧)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()。按上述顺序使用这些方法是非常重要的。让我们看看如果顺序颠倒会发生什么。这种情况需要两个(或更多)线程执行相同的操作来演示。这里,我们将使用蓝色和红色的线条。
(红色) isdirectory()? — 不,需要创建
(蓝色) isdirectory()? — 不,需要创建
(红色)mkdir()? ——成功
(蓝色)mkdir()? ——失败
如你所见,蓝线程无法创建目录。但它确实是创建的,所以结果应该是积极的。如果 isdirectory() 最后调用,结果总是正确的。
隐藏的限制给定 unix 同时打开的文件数量限制在 rlimit_nofile 的值。在 android 上,这通常是 1024,但实际上你可以少用(不包括框架使用的文件描述符) android 8.0.0 上使用空 activity 测试期间,大约有 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()) } } </assetfiledescriptor>
如果你使用它,请注意#apply(),这个值不会持续很长时间——所以你不会得到任何异常。但是,持有这个值 sharedpreferences 在实例应用程序终止之前,它将能够访问。这是因为共享偏好也保存在内存中。
亡灵确实存在人们可能认为僵尸、食尸鬼和其他类似的生物只存在于幻想和恐怖小说中。但它们在计算机科学中是真实的!这些常见术语指的是僵尸过程。事实上,死亡文件也可以很容易地创建。
在类unix操作系统中,文件删除通常是通过取消链接来实现的。未链接的文件名称将从文件系统中删除(假设它是最后一个硬链接),但任何打开的文件描述符仍然有效和可用。您仍然可以阅读和写入此类文件。这是一个片段:
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?: ${file.delete()}") println("content?: ${it.readLine()}") }
还有现场演示。
包起来首先,请记住,在创建不存在的目录时,我们不能忘记正确的调用顺序。此外,请记住,同时打开的文件数量是有限的,而不仅仅是计算您打开的文件很清楚。最后但并非最不重要的是,在最后一次使用之前删除文件的技巧可以为您提供更多的灵活性。
最初于2017年9月27日发布的wwwww.thedroidsonroids.com。
以上是要记住的边缘情况零件文件的详细内容,请关注图灵教育的其他相关文章!