파일이 존재하는 동시에 존재하지 않을 수도 있다는 사실을 알고 계셨나요? 파일을 삭제하고 계속 사용할 수 있다는 사실을 알고 계셨나요? 소프트웨어 개발에서 이러한 파일과 기타 파일의 엣지 케이스를 알아보세요.
소프트웨어 개발의 극단적인 경우에 대한 이전 기사에서 저는 텍스트 트랩에 대해 글을 쓰고 이를 피하는 방법에 대한 몇 가지 제안을 제공했습니다. 이번 블로그 게시물에서는 파일 및 파일 I/O 작업에 중점을 두고자 합니다.
java.io.File API는 특히 다음 3가지 메소드를 제공합니다.
#존재()
#isDirectory()
#isFile()
존재하는 특정 경로가 가리키는 경우 객체는 파일이거나 디렉터리라고 생각할 수 있습니다. 스택 오버플로에 대한 이 질문과 같습니다. 그러나 이것이 항상 사실은 아닙니다.
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()를 선택합니다. 이러한 방법을 언급된 순서대로 사용하는 것이 중요합니다. 순서가 바뀌면 어떤 일이 일어날 수 있는지 봅시다. 이 사례를 설명하려면 동일한 작업을 수행하는 두 개 이상의 스레드가 필요합니다. 여기서는 파란색과 빨간색 실을 사용하겠습니다.
(빨간색) isDirectory()? — 아니요, 생성해야 합니다.
(파란색) isDirectory()? — 아니요, 생성해야 합니다.
(빨간색) mkdir()? - 성공
(파란색) 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() 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에 처음 게시되었습니다.
부인 성명: 제공된 모든 리소스는 부분적으로 인터넷에서 가져온 것입니다. 귀하의 저작권이나 기타 권리 및 이익이 침해된 경우 자세한 이유를 설명하고 저작권 또는 권리 및 이익에 대한 증거를 제공한 후 이메일([email protected])로 보내주십시오. 최대한 빨리 처리해 드리겠습니다.
Copyright© 2022 湘ICP备2022001581号-3