1. 파일 입출력(File Input/Output)


이번에는 파일(File)을 다루는 클래스와 메소드들을 살펴보려고 합니다. 이 파일의 사전적 의미로는 '하나의 단위로서 처리되는 서로 관련 있는 레코드의 집합'라는 의미를 가지고 있습니다. System.IO 네임스페이스를 살펴보면 파일을 읽고 쓸 수 있는 클래스가 있는데, 오늘 배우게 될 클래스를 아래에 정리해두었습니다.


클래스

설명

File

파일에 대한 생성, 복사, 삭제, 이동 및 열기를 위한 정적 메소드를 제공

FileInfo 

파일에 대한 생성, 복사, 삭제, 이동 및 열기를 위한 속성 및 인스턴스 메소드를 제공

FileStream 

파일에 대한 스트림을 제공하여 동기 및 비동기 읽기/쓰기 작업을 모두 지원

StreamReader 

문자열에서 읽어오는 TextReader를 구현함

StreamWriter 

TextWriter를 구현하여 특정 인코딩의 스트림에 문자를 씀


이 말고도 클래스는 다양하니, 더 참고하고 싶으신 분들은 아래의 MSDN를 방문하시기 바랍니다.

MSDN: http://msdn.microsoft.com/ko-kr/library/29kt2zfk.aspx


우선은 File, FileInfo, FileStream 클래스부터 알아보도록 할까요?


2. File, FileInfo, FileStream


위에서 말했듯이 File, FileInfo 클래스 내에는 파일 생성, 복사, 삭제, 이동 및 열기 등 다양한 메소드를 제공하고 있습니다. 그런데 '정적 메소드'와 '인스턴스 메소드'라는게 보이죠? 우리가 여태까지 C# 강좌를 보면서, 메소드 앞에 static 한정자가 붙은 메소드를 본적이 있는데 이런 메소드를 '정적 메소드(static method)' 라고 합니다. 정적 메소드의 특징은 클래스의 인스턴스를 따로 생성하지 않아도 호출이 가능하다는 특징을 가지고 있습니다. 반면에 인스턴스 메소드는 클래스의 인스턴스를 생성해야만 호출할 수 있는 메소드겠죠?


자, 이제는 File 클래스와 FileInfo 클래스의 생성, 복사, 삭제, 이동에서 어떠한 차이점을 보이는지 표로 정리해 두었습니다.


기능

File 

FileInfo

생성

FileStream fs = File.Create("test.txt");

FileInfo file = new FileInfo("test.txt");

FileStream fs = file.Create(); 

복사 

File.Copy("a.txt", "b.txt"); 

FileInfo src = new FileInfo("a.txt");

FileInfo dst = src.CopyTo("b.txt"); 

삭제 

File.Delete("a.txt"); 

FileInfo file = new FileInfo("a.txt");

file.Delete(); 

이동 

File.Move("a.txt", "b.txt");

FileInfo file = new FileInfo("a.txt");

file.MoveTo("b.txt");


위 표를 잘보시면 File의 생성 부분에 FileStream이라는 클래스가 쓰였죠? FIleStream 클래스에 대해 알아보기 전에, 스트림(Stream)이란 C#에서의 입출력 작업을 도와주는 중간 매개체 역할을 하는 녀석입니다. 우리가 파일을 주고 받을때도, 모니터, 키보드 등으로 데이터를 주고 받는 작업 등 이 모든게 스트림(Stream)을 이용한 것입니다. 만약에, 스트림을 이용하지 않는다면 얼마나 끔찍할지 상상이 안갑니다. 그럼 FileStream 클래스는 무슨 역할을 하는 것일까요? 이 FileStream 클래스는 파일에서 스트림을 생성할때 사용되며, 바이트 배열로 읽고 쓰기 위해 사용되는 클래스입니다. (입출력의 성능 향상을 위해 버퍼링을 지원합니다.)


이제, File 클래스와 FileInfo 클래스를 직접 사용해보도록 합시다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication46
{
    class Program
    {
        static void Main(string[] args)
        {
            FileStream fsa = File.Create("a.txt");
            FileInfo file = new FileInfo("b.txt");
            FileStream fsb = file.Create();

            fsa.Close();
            fsb.Close();
            File.Copy("a.txt", "c.txt");
            FileInfo dst = file.CopyTo("d.txt");
        }
    }
}

코드를 보시면 14~16행에서 a.txt와 b.txt를 생성(Create) 합니다. 그런데 그 다음 줄을 보시면 Close라는 메소드가 보이는데, 이 Close 메소드는 현재 스트림을 닫는 역할을 합니다. 만약 스트림을 다 사용하고 닫아주지 않으면, 이미 사용중이기 때문에 파일에 액세스 할 수 없다는 예외가 발생합니다. 그렇기 때문에 파일을 만들고 스트림을 닫고, 20~21행에서 a.txt는 c.txt로 복사하고, b.txt는 d.txt로 복사했습니다.


프로그램을 실행한 경로를 따라가보면 a.txt, b.txt, c.txt, d.txt가 생성되었음을 보실 수 있습니다. 이번에는, 파일을 쓰고 읽을 수 있는 프로그램을 만들어 보도록 합시다.


3. StreamReader, StreamWriter


자 이번에는 특정 인코딩으로 입력된 문자를 읽도록 하는 StreamReader 클래스와, 특정 인코딩에서의 문자 출력을 위한 StreamWriter 클래스를 알아보려고 합니다. (StreamReader는 TextReader로부터, StreamWriter는 TextWriter로 부터 파생되었습니다.) 아래는 StreamReader, StreamWriter에서 주로 쓰이는 메소드를 표로 정리한 것입니다.


메소드

설명

사용 예

Read

입력 스트림의 다음 문자를 읽습니다.

Console.Write((char)sr.Read());

ReadLine 

현재 스트림에서 한 줄의 문자를 읽고 데이터를 문자열로 반환합니다.

Console.WriteLine(sr.ReadLine());

Peek 

파일 끝에 도달하거나 다른 오류가 발생했는지 확인하기 위한 정수 값을 반환합니다. 문자가 더이상 없으면 -1를 반환합니다.

while (sr.Peek() >= 0) {

   ...

}

Close

스트림을 닫고 관련된 시스템 리소스를 모두 해제합니다.

sr.Close();

<StreamReader>


메소드

설명 

사용 예 

Write 

스트림에 씁니다.

sw.Write("The date is: ");

WriteLine 

스트림에 쓰고, 마지막에 개행되어 나옵니다.

sw.WriteLine("header for the file.");

Close 

스트림을 닫고 관련된 시스템 리소스를 모두 해제합니다. 

sw.Close(); 

<StreamWriter>


이것 말고도 여러개의 메소드를 지원하니 아래의 MSDN를 방문하여 참고하실분은 참고하세요.

StreamWriter: http://msdn.microsoft.com/ko-kr/library/system.io.streamwriter_methods(v=vs.80).aspx

StreamReaderhttp://msdn.microsoft.com/ko-kr/library/system.io.streamreader(v=vs.80).aspx


이제 위의 두 클래스를 이용하여 파일을 쓰고 읽는 프로그램을 만들어 봅시다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication46
{
    class Program
    {
        static void Main(string[] args)
        {
            // StreamWriter
            StreamWriter sw = new StreamWriter("a.txt");
            sw.Write("sw.Write()");
            sw.Write(" sw.Write()");
            sw.WriteLine(" sw.WriteLine()");
            sw.WriteLine("sw.WriteLine()");

            sw.Close();

            // StreamReader
            StreamReader sr = new StreamReader("a.txt");

            while (sr.Peek() >= 0)
            {
                Console.WriteLine(sr.ReadLine());
            }
            sr.Close();
        }
    }
}

결과:

sw.Write() sw.Write() sw.WriteLine()

sw.WriteLine()

계속하려면 아무 키나 누르십시오 . . .


코드를 보시면, 15행에서 StreamWriter 객체를 생성하고, 생성함과 동시에 생성자에 스트림을 쓸 파일명을 넘깁니다. 그런 뒤에 16~19행에서는 문자열을 파일에 씁니다. 21행에선 이 스트림을 다 사용했으니 닫습니다. a.txt를 직접 열어보면 아래와 같이 입력되어 있습니다.


a.txt:

sw.Write() sw.Write() sw.WriteLine()

sw.WriteLine()



그 다음엔, 24행을 보시면 StreamReader 객체를 생성하고, 생성함과 동시에 생성자에 스트림을 읽을 파일명을 넘깁니다. 26행을 보시면 Peek 메소드가 쓰였는데, 위에서 말했듯이 파일의 끝에 도달하면 -1를 반환합니다. 파일의 끝에 도달할때까지 한줄 한줄씩 읽는것과 같습니다. 그런 뒤에 30행에서 이 스트림을 닫습니다.

StreamWriter, StreamReader를 마지막으로 C# 강좌는 여기서 마치도록 하려고 합니다. 많이 부족한 강좌였지만, 여기까지 봐주신 여러분들에게 정말 감사의 말씀을 드리고 싶습니다. 이제는 API와 C++에 대한 게시글을 중점적으로 올릴 예정이며, 만약 설명이 부족한 부분이 있어 그 부분을 덧글로 달아주시면 보충 설명을 달아드리도록 하겠습니다. 여태까지 수고하셨습니다.