본문 바로가기

.NET/C#

[C#] StreamReader와 StreamWriter로 파일 읽기, 쓰기

C#에서 파일을 읽고 쓰기 위한 스트림으로 StreamReader와 StreamWriter를 사용할 수 있다.

이 클래스들은 System.IO 네임스페이스에 선언되어 있으므로 사용하기 위해서는 System.IO를 using 해줘야 한다.

using System.IO;

1. 파일 읽기

파일을 읽기 위해서 StreamReader를 사용한다. 파일 경로를 통해 새로운 StreamReader 개체를 생성하자.

void ReadFile(string path)
{
    var sr = new StreamReader(path);
}

StreamReader의 생성자에 들어가는 path는 파일의 경로와 파일명(파일 이름 + 확장자)을 포함하는 전체 경로여야 한다. (단, 파일이 실행 프로그램(*.exe)과 같은 경로상에 존재할 경우 위 코드와 같이 파일명만으로 구성될 수도 있다.)

 

만일 파일이 존재하지 않거나, 다른 프로세스에서 이미 사용중인 파일인 경우 IOException 예외가 발생한다.

 

파일을 열었으니 이제 읽어보자. 파일을 읽는 방법은 크게 3가지가 있다.

(1) 한 문자씩 읽기 - Read()

void ReadFile(string path)
{
    var sr = new StreamReader(path);

    richTextBox1.AppendText(((char)sr.Read()).ToString());
}

 

StreamReader.Read() 메서드는 파일 스트림의 Position에서 문자 1개를 읽고 Position을 1칸 전진시킨다.

(여기서 Position은 Stream.Position으로, 스트림의 문자 위치를 의미한다. StreamReader.BaseStream.Position으로 이 값을 직접 확인해볼 수 있다.)

반환 값은 읽은 문자를 나타내는 int형 값으로, 일반적인 경우 아스키 값이므로 char로 형 변환하여 출력하면 된다. 만약 스트림의 끝을 읽은 경우 즉, 파일의 끝에 도달한 경우에는 -1을 반환한다.

 

이 방법으로 파일의 끝까지 읽기 위해서는 아래처럼 작성하면 된다.

void ReadFile(string path)
{
    var sr = new StreamReader(path);

    int c;
    while(true)
    {
        c = sr.Read();
        if(c == -1) break;
    
        richTextBox1.AppendText(((char)c).ToString());
    }

    sr.Close();
}

또는,

void ReadFile(string path)
{
    var sr = new StreamReader(path);

    int c;
    while((c = sr.Read()) != -1)
    {
        richTextBox1.AppendText(((char)c).ToString());
    }

    sr.Close();
}

이런 코드도 가능하다. 더 간결하기 때문에 필자는 이 스타일을 선호한다.

 

(2) 한 줄씩 읽기 - ReadLine()

void ReadFile(string path)
{
    var sr = new StreamReader(path);

    richTextBox1.AppendText(sr.ReadLine());
}

StreamReader.ReadLine() 메서드는 파일 스트림의 Position부터 개행 문자를 만날 때까지의 1줄의 문자열을 읽는다. 읽은 문자열의 뒤에 개행 문자는 포함되지 않는다. Position은 다음 줄의 시작 위치가 된다.

반환 값은 읽은 문자열이며, 스트림의 끝을 읽은 경우 null이다.

 

마찬가지로 아래와 같이 파일을 끝까지 읽을 수 있다.

void ReadFile(string path)
{
    var sr = new StreamReader(path);

    string line;
    while((line = sr.ReadLine()) != null)
    {
        richTextBox1.AppendText(line + Environment.NewLine);
    }

    sr.Close();
}

StreamReader.Read() 메서드는 무조건 한 문자씩 읽기 때문에 개행 문자 역시 읽는 반면, StreamReader.ReadLine() 메서드는 개행 문자를 포함하지 않으므로 파일의 내용을 그대로 옮기려면 Environment.NewLine 또는 \r\n 등 개행 문자를 직접 추가해줘야 한다.

 

(3) 전체를 한 번에 읽기 - ReadToEnd()

void ReadFile(string path)
{
    var sr = new StreamReader(path);

    richTextBox1.AppendText(sr.ReadToEnd());
}

StreamReader.ReadToEnd() 메서드는 Position부터 스트림의 끝까지를 한 번에 읽는다. 따라서 파일을 그저 읽어서 별도의 처리 없이 출력하거나 하는 등의 용도라면 ReadToEnd() 메서드로 한 번에 해결할 수 있다.

 

파일을 모두 사용했다면 반드시 Close() 메서드를 사용하여 파일 스트림을 닫아준다.

void ReadFile(string path)
{
    var sr = new StreamReader(path);

    // ... 파일 사용 ...

    sr.Close();
}

 

파일을 닫는 과정을 실수로 깜빡하는 것을 방지하기 위해 using 구문을 사용할 수 있다.

void ReadFile(string path)
{
    using (var sr = new StreamReader(path))
    {
        // ... 파일 사용 ...
    }
}

 

using 구문에 대한 설명은 아래 글의 2번 참조.

 

[C#] using 구문 사용법

using 키워드는 단순히 네임스페이스를 임포트할 때 뿐만 아니라 다른 용도로도 사용이 가능한데, using의 유용한 두 가지 기능을 소개한다. 1. 별칭 선언 using 키워드를 사용해 특정 타입의 별칭을

cs-solution.tistory.com


2. 파일 쓰기

파일에 쓰기 위해서 StreamWriter를 사용한다. 파일 경로를 통해 새로운 StreamWriter 개체를 생성하자.

void WriteFile(string path, string text)
{
    var sw = new StreamWriter(path);
}

StreamWriter의 생성자에 들어가는 path는 StreamReader와 동일한 메커니즘으로 지정하면 된다.

 

만일 파일이 존재하지 않는다면 지정된 path의 경로와 파일명으로 새로운 파일을 생성한다.

 

파일에 쓰는 방법은 2가지이다.

(1) 일반적인 쓰기 - Write()

void WriteFile(string path, string text)
{
    var sw = new StreamWriter(path);

    sw.Write(text);
}

StreamWriter.Write() 메서드는 지정된 값을 스트림에 쓰는 메서드이다. 부모 클래스의 Write() 메서드를 포함하여 총 17개의 메서드가 오버로드되어 있으며, 가장 흔히 사용되는 것은 StreamWriter.Write(string value)일 것이다.

 

Write(string value)의 경우 지정된 문자열을 그대로 스트림에 쓰게 되며, Write(int value), Write(double value) 등 텍스트가 아닌 값을 매개변수로 갖는 Write() 메서드는 지정된 값을 ToString() 메서드를 사용하여 문자열 표현으로 바꾼 뒤에 스트림에 쓴다. 가령 sw.Write(123)의 경우 정수 123의 문자열 표현인 "123"이 파일에 쓰이는 식. (물론 큰따옴표(")는 포함되지 않는다.)

 

(2) 줄 단위로 쓰기 - WriteLine()

void WriteFile(string path, string text)
{
    var sw = new StreamWriter(path);

    sw.WriteLine(text);
}

StreamWriter.WriteLine()은 StreamWriter.Write()와 사용법이 거의 동일하며, 다른 점은 문자열 뒤에 개행 문자를 하나 추가해 준다는 점이다. 즉, WriteLine()은 기본적으로 '개행문자'라는 하나의 입력값을 가지며, 이 때문에 Write()와는 다르게 매개변수가 없는 형태 또한 오버로드되어 총 18개의 메서드가 오버로드되어 있다. 매개변수가 없는 형태로 호출하게 되면 줄바꿈만 수행하게 된다.

void WriteFile(string path, string text)
{
    var sw = new StreamWriter(path);

    sw.WriteLine(text);
    sw.WriteLine();		// 줄바꿈
    sw.WriteLine(15);	// 15
}

 

StreamWriter 역시 사용이 끝난 후 Close() 메서드로 스트림을 닫아줘야 한다.

 


3. 파일에 이어서 쓰기

이미 존재하는 파일의 끝에 내용을 이어서 쓸 수 있다.

StreamReader로 이어서 쓸 파일의 내용을 먼저 읽어서 보관하고, 스트림을 닫은 후 StreamWriter로 파일을 열어서 보관해둔 문자열을 쓴 다음 이어붙이는 과정은 매우 번거롭고, 각 과정에서 적절한 예외처리를 하는 것 역시 까다로운데, StreamWriter에 기본적으로 Append 옵션이 있기 때문에 위 과정을 직접 구현할 필요가 없다.

void WriteFile(string path, string text)
{
    var sw = new StreamWriter(path, true);

    sw.Write(text);
}

StreamWriter의 오버로드된 생성자 중에 StreamWriter(string path, bool append)가 있다. 여기서 append 옵션을 위 코드와 같이 true로 하면 파일을 덮어쓰지 않고 내용을 파일의 뒤에 추가하게 된다.

 

이후에는 StreamWriter의 사용법을 그대로 사용한다.

'.NET > C#' 카테고리의 다른 글

[C#] ThreadPool 사용법  (0) 2021.08.12
[C#] FileInfo 클래스로 파일 정보 확인  (0) 2021.08.03
[C#] using 구문 사용법  (0) 2021.07.23
[C#] delegate와 delegate 체인  (0) 2021.07.20
[C#] 소켓 연결 시도에 타임아웃 넣기  (0) 2021.07.14