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

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

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

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]删除
最新教程 更多>
  • Polyfills——填充物还是缝隙? (第 1 部分)
    Polyfills——填充物还是缝隙? (第 1 部分)
    几天前,我们在组织的 Teams 聊天中收到一条优先消息,内容如下:发现安全漏洞 - 检测到 Polyfill JavaScript - HIGH。 举个例子,我在一家大型银行公司工作,你必须知道,银行和安全漏洞就像主要的敌人。因此,我们开始深入研究这个问题,并在几个小时内解决了这个问题,我将在下面...
    编程 发布于2024-11-05
  • 移位运算符和按位简写赋值
    移位运算符和按位简写赋值
    1。移位运算符 :向右移动。 >>>:无符号右移(零填充)。 2.移位运算符的一般语法 value > num-bits:将值位向右移动,保留符号位。 value >>> num-bits:通过在左侧插入零将值位向右移动。 3.左移 每次左移都会导致该值的所有位向左移动一位。 右侧插入0位。 效果:...
    编程 发布于2024-11-05
  • 如何使用 VBA 从 Excel 建立与 MySQL 数据库的连接?
    如何使用 VBA 从 Excel 建立与 MySQL 数据库的连接?
    VBA如何在Excel中连接到MySQL数据库?使用VBA连接到MySQL数据库尝试连接使用 VBA 在 Excel 中访问 MySQL 数据库有时可能具有挑战性。在您的情况下,您在尝试建立连接时遇到错误。要使用 VBA 成功连接到 MySQL 数据库,请按照下列步骤操作:Sub ConnectDB...
    编程 发布于2024-11-05
  • 测试自动化:使用 Java 和 TestNG 进行 Selenium 指南
    测试自动化:使用 Java 和 TestNG 进行 Selenium 指南
    测试自动化已成为软件开发过程中不可或缺的一部分,使团队能够提高效率、减少手动错误并以更快的速度交付高质量的产品。 Selenium 是一个用于自动化 Web 浏览器的强大工具,与 Java 的多功能性相结合,为构建可靠且可扩展的自动化测试套件提供了一个强大的框架。使用 Selenium Java 进...
    编程 发布于2024-11-05
  • 我对 DuckDuckGo 登陆页面的看法
    我对 DuckDuckGo 登陆页面的看法
    “你为什么不谷歌一下呢?”是我在对话中得到的常见答案。谷歌的无处不在甚至催生了新的动词“谷歌”。但是我编写的代码越多,我就越质疑我每天使用的数字工具。也许我对谷歌使用我的个人信息的方式不再感到满意。或者我们很多人依赖谷歌进行互联网搜索和其他应用程序,说实话,我厌倦了在搜索某个主题或产品后弹出的广告,...
    编程 发布于2024-11-05
  • 为什么 Turbo C++ 的“cin”只读取第一个字?
    为什么 Turbo C++ 的“cin”只读取第一个字?
    Turbo C 的“cin”限制:仅读取第一个单词在 Turbo C 中,“cin”输入运算符有一个处理字符数组时的限制。具体来说,它只会读取直到遇到空白字符(例如空格或换行符)。尝试读取多字输入时,这可能会导致意外行为。请考虑以下 Turbo C 代码:#include <iostream....
    编程 发布于2024-11-05
  • 使用 Buildpack 创建 Spring Boot 应用程序的 Docker 映像
    使用 Buildpack 创建 Spring Boot 应用程序的 Docker 映像
    介绍 您已经创建了一个 Spring Boot 应用程序。它在您的本地计算机上运行良好,现在您需要将该应用程序部署到其他地方。在某些平台上,您可以直接提交jar文件,它将被部署。在某些地方,您可以启动虚拟机,下载源代码,构建并运行它。但是,大多数时候您需要使用容器来部署应用程序。大...
    编程 发布于2024-11-05
  • 如何保护 PHP 代码免遭未经授权的访问?
    如何保护 PHP 代码免遭未经授权的访问?
    保护 PHP 代码免遭未经授权的访问保护 PHP 软件背后的知识产权对于防止其滥用或盗窃至关重要。为了解决这个问题,可以使用多种方法来混淆和防止未经授权的访问您的代码。一种有效的方法是利用 PHP 加速器。这些工具通过缓存频繁执行的部分来增强代码的性能。第二个好处是,它们使反编译和逆向工程代码变得更...
    编程 发布于2024-11-05
  • React:了解 React 的事件系统
    React:了解 React 的事件系统
    Overview of React's Event System What is a Synthetic Event? Synthetic events are an event-handling mechanism designed by React to ach...
    编程 发布于2024-11-05
  • 为什么在使用 Multipart/Form-Data POST 请求时会收到 301 Moved Permanently 错误?
    为什么在使用 Multipart/Form-Data POST 请求时会收到 301 Moved Permanently 错误?
    Multipart/Form-Data POSTs尝试使用 multipart/form-data POST 数据时,可能会出现类似所提供的错误消息遭遇。理解问题需要检查问题的构成。遇到的错误是 301 Moved Permanently 响应,表明资源已被永久重定向。当未为 multipart/f...
    编程 发布于2024-11-05
  • 如何使用日期和时间对象确定 PHP 中的时间边界?
    如何使用日期和时间对象确定 PHP 中的时间边界?
    确定 PHP 中的时间边界在此编程场景中,我们的任务是确定给定时间是否在预定义的范围内。具体来说,我们得到三个时间字符串:当前时间、日出和日落。我们的目标是确定当前时间是否位于日出和日落的边界时间之间。为了应对这一挑战,我们将使用 DateTime 类。这个类使我们能够表示和操作日期和时间。我们将创...
    编程 发布于2024-11-05
  • 如何使用 CSS 变换比例修复 jQuery 拖动/调整大小问题?
    如何使用 CSS 变换比例修复 jQuery 拖动/调整大小问题?
    jQuery 使用 CSS 变换缩放拖动/调整大小问题: 当应用 CSS 变换时,特别是变换:矩阵(0.5, 0, 0, 0.5, 0, 0);,对于一个 div 并在子元素上使用 jQuery 的draggable() 和 resizing() 插件,jQuery 所做的更改变得与鼠标位置“不同步...
    编程 发布于2024-11-05
  • 如何修复 TensorFlow 中的“ValueError:无法将 NumPy 数组转换为张量(不支持的对象类型浮点)”错误?
    如何修复 TensorFlow 中的“ValueError:无法将 NumPy 数组转换为张量(不支持的对象类型浮点)”错误?
    TensorFlow:解决“ValueError: Failed to Convert NumPy Array to Tensor (Unsupported Object Type Float)”工作时遇到的常见错误TensorFlow 的错误是“ValueError:无法将 NumPy 数组转换为...
    编程 发布于2024-11-05
  • 如何高效判断本地存储项是否存在?
    如何高效判断本地存储项是否存在?
    确定本地存储项目是否存在使用 Web 存储时,在访问或修改特定项目之前验证它们是否存在至关重要。在本例中,我们想要确定 localStorage 中是否设置了特定项目。当前方法检查项目是否存在的当前方法似乎是:if (!(localStorage.getItem("infiniteScro...
    编程 发布于2024-11-05
  • Java 中的原子是什么?了解 Java 中的原子性和线程安全
    Java 中的原子是什么?了解 Java 中的原子性和线程安全
    1. Java 原子简介 1.1 Java 中什么是原子? 在Java中,java.util.concurrent.atomic包提供了一组支持对单个变量进行无锁线程安全编程的类。这些类统称为原子变量。最常用的原子类包括 AtomicInteger 、 Atomic...
    编程 发布于2024-11-05

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

Copyright© 2022 湘ICP备2022001581号-3