입출력 인터페이스 사용하기

이재홍 http://www.pyrasis.com 2014.12.17 ~ 2015.02.07

Go 언어는 io.Reader, io.Writer 인터페이스를 활용하여 다양한 방법으로 입출력을 할 수 있습니다. 즉, 입출력 인터페이스를 받는 여러 패키지가 있으므로 화면에 출력하는 부분부터 파일, 문자열까지 같은 인터페이스로 처리할 수 있습니다.

io.Readerio.Writer의 정의는 다음과 같습니다.

type Reader interface {
        Read(p []byte) (n int, err error)
}

type Writer interface {
        Write(p []byte) (n int, err error)
}

어떤 구조체든 매개변수로 바이트 슬라이스를 받고, 정수와 에러 값을 리턴하는 Read 함수를 가지고 있으면 io.Reader 인터페이스를 따른다고 할 수 있습니다. 마찬가지로 매개변수로 바이트 슬라이스를 받고 정수와 에러 값을 리턴하는 Write 함수를 가지고 있으면 io.Writer 인터페이스를 따른다고 할 수 있습니다.

파일 처리하기

다음은 bufio 패키지에서 제공하는 입출력 인터페이스 함수입니다.

  • func NewReader(rd io.Reader) *Reader: io.Reader 인터페이스로 io.Reader 인터페이스 따르는 읽기 인스턴스 생성
  • func NewWriter(w io.Writer) *Writer: io.Writer 인터페이스로 io.Writer 인터페이스를 따르는 쓰기 인스턴스 생성
  • func (b *Writer) WriteString(s string) (int, error): 문자열을 버퍼에 저장
  • func (b *Writer) Flush() error: 버퍼의 데이터를 파일에 저장

bufio는 Buffered I/O를 뜻하며 io.Reader, ioWriter 인터페이스를 받습니다. 이제 bufio를 사용하여 파일을 읽고 써보겠습니다.

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	file, err := os.OpenFile(
		"hello.txt",
		os.O_CREATE|os.O_RDWR|os.O_TRUNC, // 파일이 없으면 생성,
                                                  // 읽기/쓰기, 파일을 연 뒤 내용 삭제
		os.FileMode(0644),                // 파일 권한은 644
	)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer file.Close() // main 함수가 끝나기 직전에 파일을 닫음

	w := bufio.NewWriter(file)     // io.Writer 인터페이스를 따르는 file로
                                       // io.Writer 인터페이스를 따르는 쓰기 인스턴스 w 생성
	w.WriteString("Hello, world!") // 쓰기 인스턴스로 버퍼에 Hello, world! 쓰기
	w.Flush()                      // 버퍼의 내용을 파일에 저장

	r := bufio.NewReader(file)   // io.Reader 인터페이스를 따르는 file로
                                     // io.Reader 인터페이스를 따르는 읽기 인스턴스 r 생성
	fi, _ := file.Stat()         // 파일 정보 구하기
	b := make([]byte, fi.Size()) // 파일 크기만큼 바이트 슬라이스 생성

	file.Seek(0, os.SEEK_SET) // 파일 읽기 위치를 파일의 맨 처음(0)으로 이동
	r.Read(b)                 // 읽기 인스턴스로 파일의 내용을 읽어서 b에 저장

	fmt.Println(string(b)) // 문자열로 변환하여 b의 내용 출력
}

bufio.NewWriter 함수에 file 인스턴스를 넣으면 io.Write 인스턴스를 따르는 쓰기 인스턴스를 리턴합니다(물론 file 인스턴스도 io.Writer 인터페이스를 따릅니다). 그리고 WriteString과 같은 메서드로 파일에 문자열을 저장할 수 있습니다. 단, bufio이므로 파일에 바로 저장하지 않고 임시 공간(버퍼)에 쌓아둡니다. 따라서 버퍼의 내용을 파일에 완전히 저장하려면 Flush 메서드를 사용합니다.


그림 50-1 bufio로 문자열을 파일에 저장

마찬가지로 bufio.NewReader 함수에 file 인스턴스를 넣으면 io.Reader 인터페이스를 따르는 읽기 인스턴스를 리턴합니다(file 인스턴스도 io.Reader 인터페이스도 따릅니다). 그리고 읽기 인스턴스로는 읽기 작업을 할 수 있습니다. 여기서 파일을 읽기 전에 file.Stat 함수로 파일 크기를 구해온 뒤 슬라이스를 생성합니다.

앞에서 먼저 WriteString 함수로 파일을 조작했으므로 파일 읽기/쓰기 위치(offset)가 이동하였습니다. 그러므로 file.Seek 함수로 offset을 초기화해 준 뒤 Read 메서드로 파일의 내용을 읽습니다.


그림 50-2 파일 읽기/쓰기 위치 이동

매우 큰 파일을 읽을 때는 읽을 크기만큼의 슬라이스를 할당 한 뒤 file.Seek 함수로 읽을 위치를 옮겨가면서 일부만 읽으면 됩니다.


저작권 안내

이 웹사이트에 게시된 모든 글의 무단 복제 및 도용을 금지합니다.
  • 블로그, 게시판 등에 퍼가는 것을 금지합니다.
  • 비공개 포스트에 퍼가는 것을 금지합니다.
  • 글 내용, 그림을 발췌 및 요약하는 것을 금지합니다.
  • 링크 및 SNS 공유는 허용합니다.

Published

01 June 2015