DeepintoPharo:Chapter 03
- 제 3 장 FileSystem 과 File
FileSystem 과 File
Max Leske 참여 (maxleske@gmail.com)
Pharo에서 파일을 처리하기 위한 라이브러리를 FileSystem이라 부른다. 이는 표현적이고 우아한 객체 지향 디자인을 제공한다. 본 장은 사용자의 요구 대부분을 충족시킬 수 있는 API의 주요 측면들을 제시한다.
FileSystem은 많은 사람들이 오랜 시간 열심히 일한 결과물이다. FileSystem은 처음에 Colin Putney에 의해 개발되었으며, 라이브러리는 Pharo의 구성요소(component) 대부분과 관련해 MIT 라이센스에 따라 배포되었다. Camillo Bruni는 원본 디자인 또는 API에 몇 가지 변경을 적용하였다. Camillo Bruni는 이를 Esteban Lorenzano와 Guillermo Polito의 도움을 받아 Pharo로 통합했다. FileSystem에 도움을 주신 분들의 기존 작업이 없었다면 이번 장은 존재하지 않을 것이므로, 도움을 주신 모든 분들께 감사하는 바이다.
시작하기
프레임워크는 서로 대체가 가능하고 투명하게 서로 작업이 가능한 여러 "종류"의 파일시스템(filesystem)을 지원한다. 아마 가장 흔히 사용되는 FileSystem은 자신이 하드 디스크에 보관된 파일과 의 직접 작업과 관련될 것이다. 예로 하나를 살펴보겠다.
FileSystem 클래스는 팩토리 클래스-메서드를 제공하여 여러 filesystem으로 접근성을 제공한다. disk 메시지를 FileSystem으로 전송하면 당신의 물리적 하드 드라이브에 파일 시스템이 리턴된다. 그리고 memory를 전송하면 메모리 이미지에 보관된 새 파일 시스템이 생성된다.
| working |
working := FileSystem disk workingDirectory.
→ /Users/ducasse/Workspace/FirstCircle/Pharo/20
working := FileSystem disk workingDirectory class
→ FileReference
위의 WorkingDirectory 메시지는 당신의 Pharo 이미지를 포함하는 디렉터리로의 참조를 리턴한다. 참조는 클래스 FileReference의 인스턴스다. 참조는 프레임워크의 중심(central) 객체들로서, 파일 및 디렉터리와 작업을 위한 주요 메커니즘을 제공한다.
FileSystem은 최종 사용자에게 중요한 클래스 네 가지를 정의한다: FileSystem, FileReference, FileLocator, FileSystemDirectoryEntry. FileSystem은 새 파일 시스템을 생성하기 위한 팩토리 메서드를 제공한다. FileReference는 폴더나 파일로의 참조로서, 연산을 검색하고 실행하기 위한 메서드를 제공한다. FileLocator는 늦은 바인딩(late binding) 참조이다. 파일 로케이터(file locator)로 구체적 연산을 실행하도록 요청하면 origin의 현재 위치를 살펴보고 그에 대한 경로를 resolve한다. FileSystemDirectoryEntry는 파일이나 디렉터리에 관한 추가 정보를 받을 수 있도록 해준다. 이러한 클래스들은 'FileSystemCore' 패키지에 속하며, 이번 장의 아래 부분에서 설명하고 있다.
UnixStore 또는 WindowsStore와 같은 플랫폼 특정적 클래스는 내부적 클래스이므로 사용해선 안 된다. 아래 제시한 코드 조각은 모두 FileReference 인스턴스에서 작동한다.
이제 FileSystem을 갖고 놀아보자.
직계 자손(Immediate children). 작업 디렉터리의 직계 자손을 열거하려면 아래 표현식을 실행하라:
| working |
working := FileSystem disk workingDirectory.
working children.
→ anArray(File @ /Users/ducasse/Workspace/FirstCircle/Pharo/20/.DS_Store File
@ /Users/ducasse/Workspace/FirstCircle/Pharo/20/ASAnimation.st ...)
자식들은 직접 파일과 폴더를 리턴함을 주목하라. 현재 디렉터리의 모든 자식들을 재귀적으로 접근하기 위해서는 다음과 같이 allChildren 메시지를 사용해야 한다:
working allChildren.
문자열(string character)을 파일 참조로 변환하는 것은 흔히 사용되는 유용한 연산이다. 단순히 문자열로 asFileReference를 전송하면 그에 상응하는 파일 참조를 얻을 수 있다.
'/Users/ducasse/Workspace/FirstCircle/Pharo/20' asFileReference
문자열이 기존 파일을 가리키지 않는 경우 오류가 발생됨을 명심한다. 하지만 파일의 존재 여부도 확인이 가능하다:
'foobarzork' asFileReference exists
→ false
모든 '.st' 파일. 필터링은 파일명에 일치하는 표준 패턴을 이용해 실현된다. 작업 디렉터리 내 모든 st 파일을 찾기 위해서는 아래를 실행하라:
working allChildren select: [ :each | each basename endsWith: 'st' ]
basename 메시지는 전체명으로부터 파일명을 리턴한다 (예: /foo/gloops.taz 의 파일명은 'gloops.taz'다).
주어진 파일이나 디렉터리로 접근하기. 슬래시 연산자를 이용해 작업 디렉터리 내에서 특정 파일이나 디렉터리로의 참조를 얻는다:
| working cache |
working := FileSystem disk workingDirectory.
cache := working / 'package-cache'.
부모 폴더로 접근하기. 부모 폴더를 다시 찾아가는 일은 parent 메시지를 이용하면 쉽다:
| working cache |
working := FileSystem disk workingDirectory.
cache := working / 'package-cache'.
parent := cache parent.
parent = working
→ true
디렉터리 프로퍼티로 접근하기. 당신은 요소의 다양한 프로퍼티를 확인할 수 있다. 다음과 같은 표현식을 실행하여 캐시 디렉터리를 예로 들어보겠다.
cache exists. → true
cache isSymLink. "ask if it is a symbolic link" → false
cache isFile. → false
cache isDirectory. → true
cache basename. → 'package-cache'
cache fullName
→ '/Users/ducasse/Workspace/FirstCircle/Pharo/20/package-cache'
cache parent fullName
→ '/Users/ducasse/Workspace/FirstCircle/Pharo/20/'
exists, isFile, isDirectory, basename 메서드들은 FileReference 클래스 상에서 정의된다. 파일명(basename) 없이 경로를 얻는 메시지는 존재하지 않으며, 그것을 얻기 위해선 parent fullName을 사용해야 함을 주목하라. path 메시지는 FileSystem이 내부적으로 사용하고 공개적으로 사용되어선 안 되는 Path 객체를 리턴한다.
FileSystem은 파일과 폴더를 사실상 구별하지 않기 때문에 코드는 종종 더 명확해지고 Composite 디자인 패턴의 애플리케이션으로 간주될 수 있다는 사실을 주목하라.
파일 엔트리 상태 질의하기. 파일시스템 엔트리에 관한 추가 정보를 얻기 위해서는 entry 메시지를 이용하는 FileSystemDirectoryEntry를 얻어야 한다. 파일 권한으로 접근할 수도 있음을 명심하라. 몇 가지 예를 소개하겠다:
cache entry creation. → 2012-04-25T15:11:36+02:00
cache entry creationTime → 2012-04-25T15:11:36+02:00
cache entry creationSeconds → 3512812296 2012-08-02T14:23:29+02:00
cache entry modificationTime → 2012-08-02T14:23:29+02:00
cache entry size. → 0 (directories have size 0)
cache entry permissions → rwxr-xr-x
cache entry permissions class → FileSystemPermission
cache entry permissions isWritable → true
cache entry isFile → false
cache entry isDirectory → true
위치. 프레임워크는 위치, 즉 파일이나 디렉터리를 가리키는 늦게 바인딩된(late-bound) 참조를 지원하기도 한다. 위치(location)에게 구체적 연산을 실행하도록 요청하면 참조와 같은 방식으로 행동한다. 위치를 몇 가지 소개하겠다.
FileLocator desktop.
FileLocator home.
FileLocator imageDirectory.
FileLocator vmDirectory.
이미지로 위치를 저장하고 이미지를 다른 머신이나 운영체제로 이동할 경우, 위치는 여전히 예상 디렉터리 또는 파일로 resolve할 것이다. 몇몇 파일 위치는 가상 머신에 특정적임을 주목하라.
읽기 및 쓰기 Streams 열기
파일 상에서 스트림을 열기 위해서는 다음과 같이 단순히 writeStream이나 readStream 메시지를 이용해 읽기- 또는 쓰기-스트림에 대한 참조를 요청하기만 하면 된다:
| working stream |
working := FileSystem disk workingDirectory.
stream := (working / 'foo.txt') writeStream.
stream nextPutAll: 'Hello World'.
stream close.
stream := (working / 'foo.txt') readStream.
stream contents. → 'Hello World'
stream close.
writeStream은 기존의 어떤 파일이든 오버라이드하고, readStream은 파일이 존재하지 않을 경우 예외를 던짐을 기억하길 바란다. 심지어 숙련된 프로그래머들조차 스트림을 닫는 것을 잊어버리는 실수를 흔히 범한다. 스트림을 닫는 행위는 저수준 리소스를 해제(free)시키기 때문에 훌륭한 일이다. readStreamDo: 와 writeStreamDo: 메시지는 close 를 사용하지 않아도 된다. 아래를 살펴보라:
| working |
working := FileSystem disk workingDirectory.
working / 'foo.txt' writeStreamDo: [ :stream | stream nextPutAll: 'Hello World' ].
working / 'foo.txt' readStreamDo: [ :stream | stream contents ].
어떤 경고도 제공하지 않고 파일을 쉽게 오버라이드할 수 있음을 명심하라. 아래와 같은 상황을 고려해보자:
| working |
working := FileSystem disk workingDirectory.
working / 'authors.txt' readStreamDo: [ :stream | stream contents ].
→ 'stephane alexandre damien jannik'
파일 authors.txt 는 아래와 같은 내용으로 쉽게 오버라이드가 가능하다:
FileSystem disk workingDirectory / 'authors.txt'
writeStreamDo: [ :stream | stream nextPutAll: 'bob joe'].
파일을 다시 읽어오면 희한한 결과를 제공할지 모른다:
| working |
working := FileSystem disk workingDirectory.
working / 'authors.txt' readStreamDo: [ :stream | stream contents ].
→ 'bob joee alexandre damien jannik'
openFilestream: aString writable: aBoolean 메시지를 이용해 그에 상응하는 쓰기 상태의 스트림을 얻을 수도 있다.
| stream |
stream := FileSystem disk openFileStream: 'authors.txt' writable: true.
stream nextPutAll: 'stephane alexandre damien jannik'.
다른 편리한 메서드(convenience methods)를 위한 FileReference의 스트림 프로토콜을 살펴보라.
Files와 Directories 재명명, 복사, 삭제하기
파일은 copyTo: 와 renameTo: 메시지를 이용해 복사 및 재명명이 가능하다. CopyTo: 는 다른 fileReference를 예상하는 반면 renameTo: 는 경로, 경로명, 또는 참조를 예상한다.
| working |
working := FileSystem disk workingDirectory.
working / 'foo.txt' writeStreamDo: [ :stream | stream nextPutAll: 'Hello World' ].
working / 'foo.txt' copyTo: (working / 'bar.txt').
| working |
working := FileSystem disk workingDirectory.
working / 'bar.txt' readStreamDo: [ :stream | stream contents ].
→ 'Hello World'
| working |
working := FileSystem disk workingDirectory.
working / 'foo.txt' renameTo: 'skweek.txt'.
| working |
working := FileSystem disk workingDirectory.
working / 'skweek.txt' readStreamDo: [ :stream | stream contents ].
→ 'Hello World'
디렉터리 생성. 디렉터리를 생성하기 위해서는 아래와 같이 createDirectory 메시지를 사용하라:
| working |
working := FileSystem disk workingDirectory.
backup := working / 'cache-backup'.
backup createDirectory.
backup isDirectory.
→ true
backup children.
→ #()
모두 복사하기. copyAllTo: 메시지를 이용하면 디렉터리의 내용을 모두 복사할 수 있다. 아래는 copyAllTo: 를 이용해 완전한 패키지-캐시를 해당 백업 디렉터리로 복사한다:
cache copyAllTo: backup.
대상 디렉터리가 존재하지 않을 경우 복사 이전에 생성됨을 주목한다.
삭제하기. 하나의 파일을 삭제하려면 delete: 메시지를 이용하라:
(working / 'bar.txt') delete.
전체적인 디렉터리 트리를 (수신자 포함) 삭제하려면 deleteAll을 이용하되, 이를 사용 시에는 주의를 기울여야 한다.
backup deleteAll.
주 입구점: FileReference
FileSystem은 다수의 개념과 FileSystem, Path, FileReference 등의 클래스를 기반으로 하는데, 그 중에서 최종 사용자에게 가장 중요한 것은 FileReference이다. FileReference는 파일을 조작하기 위한 연산 집합을 제공한다. 우리는 지금까지 몇 가지 기본 연산만 살펴보았다. 이번에는 좀 더 정교한 연산을 다루도록 하겠다.
디자인 수준에서 파일 참조(FileReference)는 두 가지 저수준 엔티티를 결합한다: 경로(Path)와 파일시스템(FileSystem)을 하나의 객체로 결합하여 파일을 조작하고 처리하기 위한 간단한 프로토콜을 제공한다. FileReference는 FileSystem의 많은 연산을 구현하지만 (둘 다 주로 다형적), 경로와 파일시스템을 따로 추적할 필요는 없다.
Filereference 정보 접근
먼저 basename, base, extensions와 같은 메시지를 이용해 일반 정보로 접근할 수 있는 파일 참조를 예로 들었다.
| pf |
pf := (FileSystem disk workingDirectory / 'package-cache' ) children second.
→ /Users/ducasse/Pharo/PharoHarvestingFixes/20/package-cache/AsmJitIgorStasenko.66.mcz
pf fullName
→ '/Users/ducasse/Pharo/PharoHarvestingFixes/20/package-cache/AsmJitIgorStasenko.66.mcz'
pf basename
→ 'AsmJit-IgorStasenko.66.mcz'
pf basenameWithoutExtension
→ 'AsmJit-IgorStasenko.66'
pf base
→ 'AsmJit-IgorStasenko'
pf extension
→ 'mcz'
pf extensions
→ an OrderedCollection('66' 'mcz')
Indicator(지시자). FileSystem은 파일 참조 지시자의 개념을 소개한다. 지시자는 참조의 타입을 전달하는 시각적 단서다. 현재로선 세 가지 종류의 지시자가 구현되는데, 존재하지 않는 참조에 대해 '?', 디렉터리에 대해 '/', 파일에 대해서는 빈 문자열이 있다. FileReference는 지시자를 활용하는 basenameWithIndicator 메시지를 정의한다. 아래 표현식을 통해 그 사용을 살펴보자.
pf basenameWithIndicator
→ 'AsmJit-IgorStasenko.66.mcz'
pf parent basename
→ 'package-cache'
pf parent basenameWithIndicator
→ 'package-cache/'
Path(경로). 경로의 일부로 접근할 필요가 있는 경우, pathSegments 메시지를 이용하면 전체명을 경로 요소로 줄여 문자열로서 리턴한다. 디자인 측면에서 보면 문자열은 "죽은(dead)" 객체로 간주되므로 가령 path 메시지를 이용하는 실제 객체를 다룰 때에 사용하는 편이 더 낫다.
pf pathSegments
→ #('Users' 'ducasse' 'Pharo' 'PharoHarvestingFixes' '20' 'package-cache' 'AsmJit-IgorStasenko.66.mcz')
pf path
→ Path / 'Users' / 'ducasse' / 'Pharo' / 'PharoHarvestingFixes' / '20' / 'packagecache' /'AsmJit-IgorStasenko.66.mcz'
크기. FileReference는 파일의 크기로 접근하는 방식 또한 제공한다.
pf humanReadableSize
→ '182.78 kB'
pf size
→ 182778
파일 정보. creationTime과 permissions를 이용하면 파일 엔트리 자체에 관해 제한된 정보를 얻을 수 있다. 전체 정보를 얻으려면 entry 메시지를 이용해 엔트리 자체로 접근해야 한다.
| pf |
pf := (FileSystem disk workingDirectory / 'package-cache' ) children second.
pf creationTime.
→ 2012-06-10T10:43:19+02:00
pf modificationTime.
→ 2012-06-10T10:43:19+02:00
pf permissions
→ rw-r--r--
엔트리는 단일 파일의 모든 메타데이터를 표현하는 객체들이다.
| pf |
pf := (FileSystem disk workingDirectory / 'package-cache' ) children second.
pf entry
pf parent entries
"returns all the entries of the children of the receiver"
파일 작업하기
파일에 실행하는 연산이 몇 가지 있다.
삭제하기. delete, deleteAll, deleteAllChildren 모두 수신자를 삭제하고, 수신자가 존재하지 않을 시 오류를 발생시킨다. delete는 파일을 삭제하고, deleteAll은 디렉터리와 그 내용을 삭제하며, deleteAllChildren는 디렉터리의 자식만 삭제한다. 게다가 deleteIfAbsent: 는 파일이 존재하지 않을 시 블록을 실행한다.
마지막으로 ensureDelete는 파일을 삭제하지만 파일이 존재하지 않더라도 오류를 발생시키지 않는다. 이와 유사하게 ensureDeleteAllChildren, ensureDeleteAll도 수신자가 존재하지 않더라도 예외를 발생시키지 않는다.
(FileSystem disk workingDirectory / 'paf') delete.
→ error
(FileSystem disk workingDirectory / 'fooFolder') deleteAll.
→ error
(FileSystem disk workingDirectory / 'fooFolder') ensureCreateDirectory.
(FileSystem disk workingDirectory / 'fooFolder') deleteAll.
(FileSystem disk workingDirectory / 'paf') deleteIfAbsent: [Warning signal: 'File did not exist'].
(FileSystem disk workingDirectory / 'fooFolder2') deleteAllChildren.
→ error
(FileSystem disk workingDirectory / 'fooFolder2') ensureCreateDirectory.
(FileSystem disk workingDirectory / 'fooFolder2') deleteAllChildren.
디렉터리 생성하기. createDirectory는 새 디렉터리를 생성하는데 디렉터리가 이미 존재할 경우 오류를 발생시킨다. ensureCreateDirectory는 디렉터리가 존재하지 않는지를 검사하고, 필요 시에만 생성한다. ensureCreateFile은 필요 시 파일을 생성한다.
(FileSystem disk workingDirectory / 'paf' ) createDirectory.
[(FileSystem disk workingDirectory / 'paf' ) createDirectory] on: DirectoryExists do: [:ex|true].
→ true
(FileSystem disk workingDirectory / 'paf' ) delete.
(FileSystem disk workingDirectory / 'paf' ) ensureCreateDirectory.
(FileSystem disk workingDirectory / 'paf' ) ensureCreateDirectory.
(FileSystem disk workingDirectory / 'paf' ) isDirectory.
→ true
파일 이동/복사하기. moveTo: 메시지를 이용해 파일의 이동이 가능하며 파일 참조가 예상된다.
(FileSystem disk workingDirectory / 'targetFolder') exist
→ false
(FileSystem disk workingDirectory / 'paf') exist
→ false
(FileSystem disk workingDirectory / 'paf' ) moveTo: (FileSystem disk workingDirectory / 'targetFolder')
→ Error
(FileSystem disk workingDirectory / 'paf' ) ensureCreateFile.
(FileSystem disk workingDirectory / 'targetFolder') ensureCreateDirectory.
(FileSystem disk workingDirectory / 'paf' ) moveTo: (FileSystem disk workingDirectory / 'targetFolder' / 'paf').
(FileSystem disk workingDirectory / 'paf' ) exists.
→ false
(FileSystem disk workingDirectory / 'targetFolder' / 'paf') exists.
→ true
파일의 이동 외에 복사 또한 가능하다. 파일의 복사에는 copyAllTo: 를 이용할 수 있다. 여기서는 소스 폴더에 포함된 파일을 대상 폴더로 복사하고자 한다.
copyAllTo: 메시지는 수신자의 깊을 복사를 수행하는데, 인자(argument)가 명시한 위치로 복사된다. 수신자가 파일일 경우 파일이 복사된다. 수신자가 디렉터리일 경우 디렉터리와 그 내용이 재귀적으로 복사된다. 인자는 존재하지 않는 참조여야 한다; 인자는 복사로 인해 생성될 것이다.
(FileSystem disk workingDirectory / 'sourceFolder') createDirectory.
(FileSystem disk workingDirectory / 'sourceFolder' / 'pif') ensureCreateFile.
(FileSystem disk workingDirectory / 'sourceFolder' / 'paf') ensureCreateFile.
(FileSystem disk workingDirectory / 'targetFolder') createDirectory.
(FileSystem disk workingDirectory / 'sourceFolder') copyAllTo: (FileSystem diskworkingDirectory / 'targetFolder').
(FileSystem disk workingDirectory / 'targetFolder' / 'pif') exists.
→ true
(FileSystem disk workingDirectory / 'targetFolder' / 'paf') exists.
→ true
copyAllTo: 메시지는 단일 파일의 복사에도 사용 가능하다:
(FileSystem disk workingDirectory / 'sourceFolder') ensureCreateDirectory.
(FileSystem disk workingDirectory / 'sourceFolder' / 'pif') ensureCreateFile.
(FileSystem disk workingDirectory / 'sourceFolder' / 'paf') ensureCreateFile.
(FileSystem disk workingDirectory / 'targetFolder') ensureCreateDirectory.
(FileSystem disk workingDirectory / 'sourceFolder' / 'paf') copyAllTo: (FileSystem diskworkingDirectory / 'targetFolder' / 'paf').
(FileSystem disk workingDirectory / 'targetFolder' / 'paf') exists.
→ true.
(FileSystem disk workingDirectory / 'targetFolder' / 'pif' ) exists.
→ false
Locator(로케이터)
로케이터는 늦게 바인딩된 참조이다. 이들은 의도적으로 모호(fuzzy)하게 남겨지는데, 일부 파일 연산이 실행될 때 구체적 참조로만 resolve된다. 로케이터는 파일시스템과 경로 대신 origin과 경로로 이루어진다. origin은 추상적 파일시스템 위치로서, 사용자의 홈 디렉터리, 이미지 파일, 또는 VM 실행파일(executable)이 이에 해당된다. origin이 isFile과 같은 메시지를 수신하면 로케이터는 그 origin을 먼저 resolve한 다음 origin에 대한 경로를 resolve한다.
로케이터는 "이미지 파일과 동일한 디렉터리에 'package-cache'로 명명된 항목"과 같은 사항들을 명시할 수 있도록 만들며, 이미지가 저장된 후 가령 다른 컴퓨터 상의 디렉터리로 이동하더라도 명세를 유효하게 남겨두도록 해준다.
locator := FileLocator imageDirectory / 'package-cache'.
locator printString. → ' {imageDirectory}/package-cache'
locator resolve. → /Users/ducasse/Pharo/PharoHarvestingFixes/20/package-cache
locator isFile. → false
locator isDirectory. → true
현재 지원하는 origin은 다음과 같다:
- imageDirectory - 이미지가 상주하는 디렉터리
- image - 이미지 파일
- changes - changes 파일
- vmBinary - 가상 머신을 실행하기 위한 실행파일
- vmDirectory - VM 애플리케이션을 포함하는 디렉터리 (vmBinary의 부모는 아닐 것이다)
- home - 사용자의 홈 디렉터리
- desktop - 사용자의 데스크톱 내용을 보유하는 디렉터리
- documents - 사용자의 문서가 보관된 디렉터리(예: '/Users/colin/Documents')
애플리케이션은 고유의 origin을 정의하기도 하지만 시스템이 이를 자동으로 resolve할 수는 없을 것이다. 대신 사용자가 디렉터리를 수동으로 선택하도록 요청할 것이다. 사용자의 선택은 캐시 저장되어 향후 해결(resolution)을 요청 시 사용자의 상호작용이 요구되지 않는다.
absolutePath vs. path. absolutePath는 수신자의 절대 경로를 리턴한다. 파일 참조가 가상이 아닌 경우 path와 absolutePath 메시지는 비슷한 결과를 제공한다. 파일이 늦게 바인딩된 참조(FileLocator의 인스턴스)일 경우, absolutePath는 파일을 resolve하고 절대 경로를 리턴하는 반면 path는 아래 보이는 바와 같이 resolve 되지 않은 파일 참조를 리턴한다.
(FileLocator image parent / 'package-cache') path
→ {image}/../package-cache
(FileLocator image parent / 'package-cache') absolutePath
→ Path / 'Data' / 'Downloads' / 'Pharo-2.0' / 'package-cache'
(FileLocator image parent / 'package-cache') absolutePath
→ Path / 'Data' / 'Downloads' / 'Pharo-2.0' / 'package-cache'
References와 Locators 또한 전체적인 디렉터리 트리를 다루는 간단한 메서드들을 제공한다.
FileSystem 내부 살펴보기
그 단계에서는 파일 조작(file handing)과 관련된 자신의 요구를 충족시키기 위해 FileSystem을 문제 없이 사용할 수 있어야 한다. 이번 절에서는 FileSystem의 내부를 다루고자 한다. 우선 중요한 구현 세부 내용을 살펴볼 것인데, 가령 데이터 베이스나 원격 파일 시스템과 같은 새로운 유형의 파일 시스템을 갖고자 하는 독자들의 관심을 끌 것이다.
FileReference = FileSystem + Path
경로(path)와 파일시스템(filesystems)은 FileSystem API의 최저 수준에 해당한다. FileReference는 경로와 파일시스템을 단일 객체로 결합하는데, 이러한 객체는 앞 절에서 보인 바와 같이 파일로 작업하기 위한 단순한 프로토콜을 제공한다. 참조는 /, parent, resolve: 와 같은 메서드로 경로 프로토콜을 구현한다.
FileSystem(파일시스템)
파일시스템은 디렉터리와 파일의 계층도에 접근하기 위한 인터페이스다. "The filesystem"은 호스트 운영체제에 의해 제공되는데 DiskStore과 그 플랫폼 특정적인 서브클래스에 의해 표현된다. 하지만 사용자는 이로 직접 연결해선 안 되며 앞서 소개한 FileSystem을 이용하여 접근해야 한다. 다른 종류의 파일시스템도 가능하다. 메모리 파일시스템은 모든 파일이 이미지에 ByteArray로서 보관되는 RAM 디스크 파일시스템을 제공한다. zip 파일시스템은 zip 파일의 내용을 나타낸다.
각 파일시스템은 고유의 작업 디렉터리를 갖고 있으며, 그곳으로 전달되는 상대 경로로 resolve하는 데에 사용된다. 예를 몇 가지 들어보겠다:
fs := FileSystem memory.
fs workingDirectoryPath: (Path / 'plonk').
griffle := Path / 'plonk' / 'griffle'.
nurp := Path
*
'nurp'.
fs resolve: nurp.
→ Path/plonk/nurp
fs createDirectory: (Path / 'plonk'). → "/plonk created"
(fs writeStreamOn: griffle) close. → "/plonk/griffle created"
fs isFile: griffle. → true
fs isDirectory: griffle. → false
fs copy: griffle to: nurp. → "/plonk/griffle copied to /plonk/nurp"
fs exists: nurp. → true
fs delete: griffle. → "/plonk/griffle" deleted
fs isFile: griffle. → false
fs isDirectory: griffle. → false
Path(경로)
경로는 FileSystem API의 가장 기본적인 요소이다. 이들은 매우 추상적인 방식으로 파일시스템 경로를 표현하고, 문자열을 조작할 필요 없이 경로를 작업하기 위해 고수준 프로토콜을 제공한다. 절대 경로(/), 상대 경로(*), 파일 확장자(,), 부모 탐색(parent)을 정의하는 방법을 몇 가지 예로 들겠다. 보통은 Path를 사용하지 않아도 되지만 예를 몇 가지 소개하겠다.
| fs griffle nurp |
fs := FileSystem memory.
griffle := fs referenceTo: (Path / 'plonk' / 'griffle').
nurp := fs referenceTo: (Path
*
'nurp').
griffle isFile.
→ false
griffle isDirectory.
→ false
griffle parent ensureCreateDirectory.
griffle ensureCreateFile.
griffle exists & griffle isFile.
→ true
griffle copyTo: nurp.
nurp exists.
→ true
griffle delete
"absolute path"
Path / 'plonk' / 'feep' → /plonk/feep
"relative path"
Path * 'plonk' / 'feep' → plonk/feep
"relative path with extension"
Path * 'griffle' , 'txt' → griffle.txt
"changing the extension"
Path * 'griffle.txt' , 'jpeg' → griffle.jpeg
"parent directory"
(Path / 'plonk' / 'griffle') parent → /plonk
"resolving a relative path"
(Path / 'plonk' / 'griffle') resolve: (Path * '..' / 'feep')
→ /plonk/feep
"resolving an absolute path"
(Path / 'plonk' / 'griffle') resolve: (Path / 'feep')
→ /feep
"resolving a string"
(Path * 'griffle') resolve: 'plonk' → griffle/plonk
"comparing"
(Path / 'plonk') contains: (Path / 'griffle' / 'nurp')
→ false
경로 프로토콜 중 일부(/, parent, resolve: 와 같은 메시지)는 참조 상에서도 이용 가능함을 명심한다.
Visitors
위의 메서드는 많은 공통 업무에 있어 충분하지만 애플리케이션 개발자는 디렉터리 트리에 좀 더 정교한 연산을 실행해야 하는 경우가 있음을 발견한다.
방문자(Visitor) 프로토콜은 매우 간단하다. 방문자는 visitFile: 와 visitDirectory를 구현할 필요가 있다. 파일시스템의 실제 순회는 가이드(guide)에 의해 처리된다. 가이드는 방문자와 함께 작업하며, 파일시스템을 crawl하고, 방문자에게 그것이 발견하는 파일과 디렉터리를 알려준다. Guide 클래스로는 세 가지가 있는데, PreorderGuide, PostorderGuide, BreathFirstGuide로서, 파일시스템을 각기 다른 순서로 순회한다. 가이드가 특정 방문자로 파일시스템을 순회하도록 만들기란 간단하다. 예를 들어보겠다:
BreadthFirstGuide show: aReference to: aVisitor
위에 설명된 열거형 메서드들은 방문자들을 이용해 구현된다: 그 예는 CopyVisitor, DeleteVisitor, CollectVisitor를 참고한다.
요약
FileSystem은 파일을 조작하기 위한 강력하고도 명쾌한 라이브러리다. 이는 Pharo에서 기본적인 부분에 해당한다. Pharo 공동체는 계속해서 이를 확장하고 빌드할 것이다. FileReference 클래스는 프레임워크에 가장 중요한 진입점이다.
- FileSystem 은 하드 디스크 상에, 그리고 메모리 내에 파일 시스템을 빌드하기 위한 팩토리 클래스 메서드를 제공한다.
- FileReference 는 프레임워크에서 중심 클래스로서, 파일이나 폴더를 나타낸다. 파일 참조는 파일을 작업하고 파일 시스템 내부를 탐색하기 위한 메서드를 제공한다.
- asFileReference 메시지를 문자열(string character)로 전송하면 그에 상응하는 파일 참조를 리턴한다 (예: '/tmp' asFileReference)
- 새 파일을 생성하여 그 내부에 작성하는 작업은 다음과 같이 간단하다: (FileSystem disk workingDirectory / 'foo.txt') writeStreamDo: [ :stream | stream nextPutAll: 'Hello World' ].
- FileLocator 는 늦은 바인딩 참조로서, 실행 중인 컨텍스트에 따라 하드 디스크 내의 파일 위치가 좌우될 때 유용하다.
- FileSystemDirectoryEntry 는 주어진 파일에 대한 저수준 세부 내용의 큰 집합을 제공한다.