」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > 要記住的邊緣情況。零件文件

要記住的邊緣情況。零件文件

發佈於2024-08-14
瀏覽:538

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]刪除
最新教學 更多>
  • C ++如何實現類型的擦除:技術的比較?
    C ++如何實現類型的擦除:技術的比較?
    在c 虛擬函數使用模板功能,將實際對象存儲在void*指針中。 boost.function利用此技術隱藏函數的真實類型。 基於template基於template的類型擦除而無需虛擬函數或void* manipulation ,Gman的方法利用模板隱藏實際類型不訴諸虛擬函數或void*操縱。 ...
    程式設計 發佈於2025-02-07
  • 如何在Java字符串中有效替換多個子字符串?
    如何在Java字符串中有效替換多個子字符串?
    Exploiting Regular ExpressionsA more efficient solution involves leveraging regular expressions.正則表達式允許您定義復雜的搜索模式並在單個操作中執行文本轉換。 示例示例usage 接下來,您可以使用匹配...
    程式設計 發佈於2025-02-07
  • 如何使用char_length()在mySQL中按字符串長度對數據進行排序?
    如何使用char_length()在mySQL中按字符串長度對數據進行排序?
    [2使用內置的char_length()function。 char_length()和length() 此查詢將從指定的表中檢索所有行,並基於上升順序對它們進行排序指定列的字符長度。帶有更長字符串的行將出現在結果的底部。
    程式設計 發佈於2025-02-07
  • 如何從PHP捲曲響應中提取cookie中的cookie?
    如何從PHP捲曲響應中提取cookie中的cookie?
    從php curl響應中檢索cookie在某些情況下,在某些情況下,在某些情況下,外部API響應可能會不足地嵌入HTTP Header中的cookie ,而不是利用諸如肥皂或休息之類的常規通信協議。為了促進這些cookie將這些餅乾提取到結構化的陣列中而無需求助於艱辛的解析,可以採用以下技術。 *...
    程式設計 發佈於2025-02-07
  • 'exec()
    'exec()
    Exec對本地變量的影響: exec function,python staple,用於動態代碼執行的python staple,提出一個有趣的Query:它可以在函數中更新局部變量嗎? python 3 Dialemma 在Python 3中,以下代碼shippet無法更新本地變量,因為人...
    程式設計 發佈於2025-02-07
  • 如何使用PHP顯示在MySQL中存儲的圖像?
    如何使用PHP顯示在MySQL中存儲的圖像?
    檢索並顯示在php 答案:是的,使用PHP有幾種方法可以實現此轉換,取決於已安裝的圖像庫。 使用GD庫:Using the ImageMagick (iMagick) Library:
    程式設計 發佈於2025-02-07
  • 如何使用替換指令在GO MOD中解析模塊路徑差異?
    如何使用替換指令在GO MOD中解析模塊路徑差異?
    克服go mod中的模塊路徑差異 coreos/bbolt:github.com/coreos/ [email受保護]:解析go.mod:模塊將其路徑聲明為:go.etcd.io/bbolt `要解決此問題,您可以在go.mod文件中使用替換指令。只需在go.mod的末尾添加以下行:[&& &...
    程式設計 發佈於2025-02-07
  • 如何在Java列表中有效計算元素的發生?
    如何在Java列表中有效計算元素的發生?
    計數列表中的元素出現在列表 中,在java編程中,列舉列表中列舉元素出現的任務來自列表。為此,收集框架提供了全面的工具套件。 在這種情況下,Batocurrences變量將保持值3,代表動物列表中的“ BAT”出現的數量。 &&& [此方法是簡單的,可以得出準確的結果,使其成為計算列表中元素出現的...
    程式設計 發佈於2025-02-07
  • 我可以將加密從McRypt遷移到OpenSSL,並使用OpenSSL遷移MCRYPT加密數據?
    我可以將加密從McRypt遷移到OpenSSL,並使用OpenSSL遷移MCRYPT加密數據?
    將我的加密庫從mcrypt升級到openssl 問題:是否可以將我的加密庫從McRypt升級到OpenSSL?如果是這樣?使用openssl? 答案:可以使用mcrypt數據加密數據,可以使用openssl。關於如何使用openssl對McRypt進行加密的數據: openssl_decryp...
    程式設計 發佈於2025-02-07
  • \“(1)vs.(;;):編譯器優化是否消除了性能差異?\”
    \“(1)vs.(;;):編譯器優化是否消除了性能差異?\”
    使用(1)而不是(;;)會導致無限循環的性能差異? 現代編譯器,(1)和(;;)之間沒有性能差異。 是如何實現這些循環的技術分析在編譯器中: perl: S-> 7 8 unstack v-> 4 -e語法ok 在GCC中,兩者都循環到相同的彙編代碼中,如下所示:。 globl t_時 ...
    程式設計 發佈於2025-02-07
  • 在映射到MySQL枚舉列時,如何確保冬眠保留值?
    在映射到MySQL枚舉列時,如何確保冬眠保留值?
    在hibernate中保存枚舉值:故障排除錯誤的列type ,他們各自的映射至關重要。在Java中使用枚舉類型時,至關重要的是,建立冬眠的方式如何映射到基礎數據庫。 在您的情況下,您已將MySQL列定義為枚舉,並在Java中創建了相應的枚舉代碼。但是,您遇到以下錯誤:“ MyApp中的錯誤列類型...
    程式設計 發佈於2025-02-07
  • 版本5.6.5之前,使用current_timestamp與時間戳列的current_timestamp與時間戳列有什麼限制?
    版本5.6.5之前,使用current_timestamp與時間戳列的current_timestamp與時間戳列有什麼限制?
    在默認值中使用current_timestamp或mysql版本中的current_timestamp或在5.6.5 這種限制源於遺產實現的關注,這些限制需要為Current_timestamp功能提供特定的實現。消息和相關問題 `Productid` int(10)unsigned not ...
    程式設計 發佈於2025-02-07
  • 如何可靠地檢查MySQL表中的列存在?
    如何可靠地檢查MySQL表中的列存在?
    在mySQL中確定列中的列存在,驗證表中的列存在與與之相比有點困惑其他數據庫系統。常用的方法:如果存在(從信息_schema.columns select * * where table_name ='prefix_topic'和column_name =&...
    程式設計 發佈於2025-02-07
  • 如何使用PHP將斑點(圖像)正確插入MySQL?
    如何使用PHP將斑點(圖像)正確插入MySQL?
    在嘗試將image存儲在mysql數據庫中時,您可能會遇到一個可能會遇到問題。本指南將提供成功存儲您的圖像數據的解決方案。 easudy values('$ this-> ; image_id','file_get_contents($ tmp_imag...
    程式設計 發佈於2025-02-07
  • 插入資料時如何修復「常規錯誤:2006 MySQL 伺服器已消失」?
    插入資料時如何修復「常規錯誤:2006 MySQL 伺服器已消失」?
    插入記錄時如何解決“一般錯誤:2006 MySQL 服務器已消失”介紹:將數據插入MySQL 數據庫有時會導致錯誤“一般錯誤:2006 MySQL 服務器已消失”。當與服務器的連接丟失時會出現此錯誤,通常是由於 MySQL 配置中的兩個變量之一所致。 解決方案:解決此錯誤的關鍵是調整wait_tim...
    程式設計 發佈於2025-02-07

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3