SqueakByExample:10.4

From 흡혈양파의 번역工房
Jump to navigation Jump to search

파일 접근에 대한 stream 의 활용

컬렉션을 stream 으로 처리하는 방법은 이미 배웠습니다. 동일한 방법으로 하드디스크에 있는 파일도 stream 으로 처리할 수 있습니다. 일단 만들어지만, 파일에 대한 stream 은 콜렉션에 대한 stream 과 거의 비슷합니다: 같은 프로토콜을 이용해서 stream 에 대한 읽기, 쓰기, 위치결정 등의 작업을 할 수 있습니다. 가장 큰 차이는 stream 을 만드는 방법에 있습니다. 파일에 대한 stream 을 만드는 방법에 대해서, 몇가지 서로 다른 방법을 살펴보겠습니다.


파일 stream 만들기

파일 stream 을 만들기 위해서는, FileStream 클래스 에서 제공되는 아래의 인스턴스 생성 메서드들 중 하나를 반드시 사용해야 합니다:

fileNamed: 주어진 이름으로 파일을 읽기, 쓰기 작업을 위해서 open 힙니다. 만약 파일이 이미 존재하는 경우, 이전의 내용은 변경하거나 옮기는게 가능합니다만, close 하는 경우 파일이 불완전한 상태가 되지는 않습니다. 만약 인수로 받은 이름에 별도로 디렉토리에 대한 내용이 포함되어있지 않다면 파일은 기본디렉토리에서 생성됩니다.

newFileNamed: 주어진 이름으로 새로운 파일을 만들고, 그 파일에 대해 쓰기 작업을 위한 stream 을 반환합니다. 만약 파일이 이미 존재한다면, 유저에게 해야할 일을 요청합니다.

forceNewFileNamed: 주어진 이름으로 새로운 파일을 만들고, 그 파일에 대해 쓰기 작업을 위한 stream 을 반환합니다. 만약 파일이 이미 존재한다면, 질문없이 해당파일을 삭제하고 새로운 파일을 생성합니다.

oldFileNamed: 읽기와 쓰기 작업을 위해 주어진 이름으로 기존에 존재하는 파일을 open 합니다[1]. 만약 파일이 이미 존재하는 경우, 이전의 내용은 변경하거나 옮기는게 가능합니다만, close 하는 경우 파일이 불완전한 상태가 되지는 않습니다. 만약 인수로 받은 이름에 별도로 디렉토리에 대한 내용이 포함되어있지 않다면 파일은 기본디렉토리에서 생성됩니다.

readOnlyFileNamed: 읽기 작업만을 위해 주어진 이름으로 이미 존재하고 있는 파일을 엽니다.


open 된 파일 stream 은 꼭 닫아주어야 합니다. 이런 닫기작업에는 close 메서드를 사용합니다.

stream := FileStream forceNewFileNamed: 'test.txt'.
stream
  nextPutAll: 'This text is written in a file named ';
  print: stream localName.
stream close.

stream := FileStream readOnlyFileNamed: 'test.txt'.
stream contents.        'This text is written in a file named ''test.txt'''
stream close.


메서드 localName 은 파일 이름의 마지막 요소를 반환합니다. 메서드 fullName 을 이용해서 전체 경로를 얻을 수도 있습니다.


아마도 당신은 머지않아 파일 stream 을 수동으로 닫는것이 꽤나 번거롭고, 에러가 발생될 수도 있다는걸 알게 될겁니다. 이런 문제때문에 FileStream 클래스는 forceNewFileNamed:do: 라는 메시지를 제공해서 작성 작업을 블록형태의 인자로 받아서 작성 작업을 끝낸후, 자동으로 stream 을 close 합니다.

FileStream
  forceNewFileNamed: 'test.txt'
  do: [:stream |
    stream
      nextPutAll: 'This text is written in a file named ';
      print: stream localName].
string := FileStream
      readOnlyFileNamed: 'test.txt'
      do: [:stream | stream contents].
string        'This text is written in a file named ''test.txt'''


파일 stream 을 만드는 메서드중 블록을 인자로 취하는 경우는, 일단 stream 만든 후, stream 을 인자로 블록을 실행하고 마지막에 stream 을 close 합니다. 이런 종류의 메서드는 블록에서 반환되는 내용을 반환하게 되며, 이것은 곧 블록의 마지막에 있는 표현식의 값이 됩니다. 위의 예제에서는 블록의 마지막 표현식은 stream contents 를 실행해서 파일의 내용을 얻은다음, 그 내용을 string 이라는 변수에 대입하고 있습니다.


Binary Stream

기본적으로 파일 stream 은, text 기반으로서 chracter 의 읽기, 쓰기 작업용으로 만들어집니다. 만약 stream 에서 binary 데이터를 취급해야한다면, 반드시 stream 에 binary 메시지를 보내야 합니다.

stream 이 binary mode 인 경우, 0 에서 255(1 byte)의 값만 사용할 수 있습니다. nextPutAll: 메서드를 이용해서 한번에 2 개 이상의 값을 쓰기write 하고싶다면 이 메서드의 인자로 ByteArray 를 넘겨주어야 합니다.

FileStream
  forceNewFileNamed: 'test.bin'
  do: [:stream |
    stream
      binary;
      nextPutAll: #(145 250 139 98) asByteArray].

FileStream
  readOnlyFileNamed: 'test.bin'
  do: [:stream |
    stream binary.
    stream size.           4
    stream next.           145
    stream upToEnd.        a ByteArray(250 139 98)
  ].


아래의 예제에서 "test.pgm" 이라고 하는 그림파일을 만들어 보도록 하겠습니다. 작성한 파일은 원하는 draw 프로그램으로 열 수 있습니다[2].

FileStream
  forceNewFileNamed: 'test.pgm'
  do: [:stream |
    stream
      nextPutAll: 'P5'; cr;
      nextPutAll: '4 4'; cr;
      nextPutAll: '255'; cr;
      binary;
      nextPutAll: #(255 0 255 0) asByteArray;
      nextPutAll: #(0 255 0 255) asByteArray;
      nextPutAll: #(255 0 255 0) asByteArray;
      nextPutAll: #(0 255 0 255) asByteArray
  ]


위의 프로그램으로 그림 10.12 와 같은 4x4 의 체스판을 만들 수 있습니다.

그림 10.12: binary stream 을 사용해서 4x4 체스판을 그릴 수 있습니다.


Notes

  1. Pharo by example 일본어판 에서는 파일이 실제로 존재하지 않는경우에 대한 동작을 사용자에게 질문한다고 되어있습니다. 아마도 squeak by example 에서는 당연한 action 이라고 생각하고 언급을 안한거같기는 하군요
  2. pharo by example 일본어판 에서는 PGM:Portablegraymap 이라는 내용이 있습니다. GIMP 등의 프로그램에서 이 포맷의 사용이 가능합니다.