”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 要记住的边缘情况。零件文件

要记住的边缘情况。零件文件

发布于2024-08-14
浏览:997

Edge Cases to Keep in Mind. Part  Files

你知道吗,可能存在一个文件同时存在又不存在?您是否知道,您可以删除文件并仍然使用它?发现软件开发中的这些和其他文件边缘情况。

在我之前关于软件开发中的边缘情况的文章中,我写了有关文本陷阱的文章,并给了您一些建议,以及如何避免它们。在这篇博文中,我想重点讨论文件和文件 I/O 操作。

一个不是文件的文件

java.io.File API 提供以下 3 种方法:

  • #exists()

  • #isDirectory()

  • #isFile()

人们可能会认为,如果它由存在的给定路径指向,则对象要么是文件,要么是目录——就像 Stack Overflow 上的这个问题一样。然而,这并不总是正确的。

File#isFile() javadocs 中没有明确提及,但 file **there确实意味着**常规文件。因此,特殊的 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()。按上述顺序使用这些方法非常重要。让我们看看如果顺序颠倒会发生什么。需要两个(或更多)线程执行相同的操作来演示这种情况。在这里,我们将使用蓝色和红色线程。

  1. (红色) isDirectory()? — 不,需要创建

  2. (蓝色) isDirectory()? — 不,需要创建

  3. (红色)mkdir()? - 成功

  4. (蓝色)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()
        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 实例的应用程序进程被终止之前,它将可以访问。这是因为共享首选项也保存在内存中。

亡灵确实存在

人们可能认为僵尸、食尸鬼和其他类似的生物只存在于奇幻和恐怖小说中。但是……它们在计算机科学中是真实存在的!这些常见术语指的是僵尸进程。其实亡灵文件也可以轻松制作。

在类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?: ${it.readLine()}")
}

还有现场演示。

包起来

首先,请记住,在创建不存在的目录时,我们不能忘记正确的方法调用顺序。此外,请记住,同时打开的文件数量是有限的,并且不仅计算您明确打开的文件。最后但并非最不重要的一点是,在最后一次使用之前删除文件的技巧可以为您提供更多的灵活性。

最初于 2017 年 9 月 27 日发布于 www.thedroidsonroids.com。

版本声明 本文转载于:https://dev.to/koral/edge-cases-to-keep-in-mind-part-2-files-53?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 为什么PHP的DateTime :: Modify('+1个月')会产生意外的结果?
    为什么PHP的DateTime :: Modify('+1个月')会产生意外的结果?
    使用php dateTime修改月份:发现预期的行为在使用PHP的DateTime类时,添加或减去几个月可能并不总是会产生预期的结果。正如文档所警告的那样,“当心”这些操作的“不像看起来那样直观。 考虑文档中给出的示例:这是内部发生的事情: 现在在3月3日添加另一个月,因为2月在2001年只有2...
    编程 发布于2025-05-25
  • CSS可以根据任何属性值来定位HTML元素吗?
    CSS可以根据任何属性值来定位HTML元素吗?
    靶向html元素,在CSS 中使用任何属性值,在CSS中,可以基于特定属性(如下所示)基于特定属性的基于特定属性的emants目标元素: 字体家庭:康斯拉斯(Consolas); } 但是,出现一个常见的问题:元素可以根据任何属性值而定位吗?本文探讨了此主题。的目标元素有任何任何属性值,属...
    编程 发布于2025-05-25
  • 同实例无需转储复制MySQL数据库方法
    同实例无需转储复制MySQL数据库方法
    在同一实例上复制一个MySQL数据库而无需转储在同一mySQL实例上复制数据库,而无需创建InterMediate sqql script。以下方法为传统的转储和IMPORT过程提供了更简单的替代方法。 直接管道数据 MySQL手动概述了一种允许将mysqldump直接输出到MySQL clie...
    编程 发布于2025-05-25
  • 如何有效地选择熊猫数据框中的列?
    如何有效地选择熊猫数据框中的列?
    在处理数据操作任务时,在Pandas DataFrames 中选择列时,选择特定列的必要条件是必要的。在Pandas中,选择列的各种选项。选项1:使用列名 如果已知列索引,请使用ILOC函数选择它们。请注意,python索引基于零。 df1 = df.iloc [:,0:2]#使用索引0和1 的 ...
    编程 发布于2025-05-25
  • 如何在Java中正确显示“ DD/MM/YYYY HH:MM:SS.SS”格式的当前日期和时间?
    如何在Java中正确显示“ DD/MM/YYYY HH:MM:SS.SS”格式的当前日期和时间?
    如何在“ dd/mm/yyyy hh:mm:mm:ss.ss”格式“ gormat 解决方案:的,请访问量很大,并应为procectiquiestate的,并在整个代码上正确格式不多: java.text.simpledateformat; 导入java.util.calendar; 导入java...
    编程 发布于2025-05-25
  • 表单刷新后如何防止重复提交?
    表单刷新后如何防止重复提交?
    在Web开发中预防重复提交 在表格提交后刷新页面时,遇到重复提交的问题是常见的。要解决这个问题,请考虑以下方法: 想象一下具有这样的代码段,看起来像这样的代码段:)){ //数据库操作... 回声“操作完成”; 死(); } ?> ...
    编程 发布于2025-05-25
  • JavaScript计算两个日期之间天数的方法
    JavaScript计算两个日期之间天数的方法
    How to Calculate the Difference Between Dates in JavascriptAs you attempt to determine the difference between two dates in Javascript, consider this s...
    编程 发布于2025-05-25
  • PHP未来:适应与创新
    PHP未来:适应与创新
    PHP的未来将通过适应新技术趋势和引入创新特性来实现:1)适应云计算、容器化和微服务架构,支持Docker和Kubernetes;2)引入JIT编译器和枚举类型,提升性能和数据处理效率;3)持续优化性能和推广最佳实践。 引言在编程世界中,PHP一直是网页开发的中流砥柱。作为一个从1994年就开始发展...
    编程 发布于2025-05-25
  • 如何将来自三个MySQL表的数据组合到新表中?
    如何将来自三个MySQL表的数据组合到新表中?
    mysql:从三个表和列的新表创建新表 答案:为了实现这一目标,您可以利用一个3-way Join。 选择p。*,d.content作为年龄 来自人为p的人 加入d.person_id = p.id上的d的详细信息 加入T.Id = d.detail_id的分类法 其中t.taxonomy =...
    编程 发布于2025-05-25
  • 为什么不使用CSS`content'属性显示图像?
    为什么不使用CSS`content'属性显示图像?
    在Firefox extemers属性为某些图像很大,&& && && &&华倍华倍[华氏华倍华氏度]很少见,却是某些浏览属性很少,尤其是特定于Firefox的某些浏览器未能在使用内容属性引用时未能显示图像的情况。这可以在提供的CSS类中看到:。googlepic { 内容:url(&#...
    编程 发布于2025-05-25
  • 如何在Java字符串中有效替换多个子字符串?
    如何在Java字符串中有效替换多个子字符串?
    在java 中有效地替换多个substring,需要在需要替换一个字符串中的多个substring的情况下,很容易求助于重复应用字符串的刺激力量。 However, this can be inefficient for large strings or when working with nu...
    编程 发布于2025-05-25
  • 如何使用替换指令在GO MOD中解析模块路径差异?
    如何使用替换指令在GO MOD中解析模块路径差异?
    在使用GO MOD时,在GO MOD 中克服模块路径差异时,可能会遇到冲突,其中可能会遇到一个冲突,其中3派对软件包将另一个带有导入套件的path package the Imptioned package the Imptioned package the Imported tocted pac...
    编程 发布于2025-05-25
  • 如何避免Go语言切片时的内存泄漏?
    如何避免Go语言切片时的内存泄漏?
    ,a [j:] ...虽然通常有效,但如果使用指针,可能会导致内存泄漏。这是因为原始的备份阵列保持完整,这意味着新切片外部指针引用的任何对象仍然可能占据内存。 copy(a [i:] 对于k,n:= len(a)-j i,len(a); k
    编程 发布于2025-05-25
  • 如何使用node-mysql在单个查询中执行多个SQL语句?
    如何使用node-mysql在单个查询中执行多个SQL语句?
    Multi-Statement Query Support in Node-MySQLIn Node.js, the question arises when executing multiple SQL statements in a single query using the node-mys...
    编程 发布于2025-05-25
  • 如何使用Python有效地以相反顺序读取大型文件?
    如何使用Python有效地以相反顺序读取大型文件?
    在python 中,如果您使用一个大文件,并且需要从最后一行读取其内容,则在第一行到第一行,Python的内置功能可能不合适。这是解决此任务的有效解决方案:反向行读取器生成器 == ord('\ n'): 缓冲区=缓冲区[:-1] ...
    编程 发布于2025-05-25

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3