> Java > java지도 시간 > 본문

명심해야 할 극단적인 경우. 부품 파일

PHPz
풀어 주다: 2024-08-09 07:05:22
원래의
390명이 탐색했습니다.

Edge Cases to Keep in Mind. Part  Files

존재하는 파일과 존재하지 않는 파일이 동시에 있을 수 있다는 사실을 알고 계셨나요? 파일을 삭제하고 계속 사용할 수 있다는 사실을 알고 계셨나요? 소프트웨어 개발에서 이러한 파일과 기타 파일의 특별한 사례를 찾아보세요.

소프트웨어 개발의 극단적인 경우에 대한 이전 기사에서 저는 텍스트 트랩에 대해 글을 썼고 이를 방지하는 방법에 대한 몇 가지 제안을 제공했습니다. 이번 블로그 게시물에서는 파일 및 파일 I/O 작업에 중점을 두고자 합니다.

파일이 아닌 파일

java.io.File API는 특히 다음 3가지 메소드를 제공합니다.

  • #존재()

  • #isDirectory()

  • #isFile()

Stack Overflow에 대한 이 질문처럼 존재하는 특정 경로가 가리키는 경우 개체는 파일이거나 디렉터리라고 생각할 수 있습니다. 하지만 항상 그런 것은 아닙니다.

File#isFile() javadocs에는 명시적으로 언급되어 있지 않지만 file **실제로는 **일반 파일을 의미합니다. 따라서 장치, 소켓 및 파이프와 같은 특수 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에서 빈 활동으로 테스트하는 동안 사용할 수 있는 파일 설명자는 약 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())
    }
}
로그인 후 복사

#apply()를 사용하면 값이 영구적으로 저장되지 않으므로 예외가 발생하지 않습니다. 그러나 SharedPreferences 인스턴스를 보유하는 앱 프로세스가 종료될 때까지 액세스할 수 있습니다. 공유된 기본 설정도 메모리에 저장되기 때문입니다.

언데드가 실제로 존재한다

좀비, 구울 및 기타 유사한 생물은 판타지와 공포 소설에만 존재한다고 생각할 수도 있습니다. 하지만… 컴퓨터 과학에서는 현실이 됩니다! 이러한 일반적인 용어는 좀비 프로세스를 나타냅니다. 실제로 언데드 파일도 쉽게 생성할 수 있습니다.

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()}")
}
로그인 후 복사

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.

위 내용은 명심해야 할 극단적인 경우. 부품 파일의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:dev.to
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿
회사 소개 부인 성명 Sitemap
PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!