Study/C#

C# 7편: 파일 입출력(IO)로 데이터 읽고 쓰기

wawManager 2024. 10. 26. 10:26
728x90

1. 파일 입출력(IO) 개요

파일 입출력(Input/Output, IO)은 프로그램이 외부 파일과 데이터를 주고받는 기능을 의미합니다. C#에서는 System.IO 네임스페이스를 사용하여 파일에 데이터를 읽고 쓰는 작업을 할 수 있습니다. 이를 통해 텍스트 파일, 바이너리 파일 등을 관리할 수 있으며, 파일 입출력은 파일의 내용 저장, 로그 기록, 데이터 백업 등의 상황에서 유용하게 사용됩니다.

2. 파일 읽기와 쓰기 기본

파일 입출력을 수행하려면 파일을 읽고 쓰는 작업을 위한 클래스와 메서드를 사용해야 합니다. C#에서는 주로 File, StreamReader, StreamWriter, FileStream 등의 클래스를 사용합니다.

3. 텍스트 파일 쓰기

File.WriteAllText 메서드

File.WriteAllText 메서드는 파일에 문자열을 간단하게 작성할 수 있습니다. 파일이 없으면 새 파일을 생성하고, 있으면 덮어씁니다.

using System;
using System.IO;

class Program
{
    static void Main(string[] args)
    {
        string path = "example.txt";
        string content = "Hello, World!";
        
        File.WriteAllText(path, content); // 파일에 텍스트 작성
        Console.WriteLine("파일에 데이터가 작성되었습니다.");
    }
}

위 코드에서는 "example.txt"라는 파일에 "Hello, World!"라는 문자열이 작성됩니다.

StreamWriter 사용

StreamWriter는 파일에 텍스트 데이터를 쓰는 데 사용하는 클래스로, 한 줄씩 또는 여러 줄에 걸쳐 데이터를 작성할 수 있습니다.

using System;
using System.IO;

class Program
{
    static void Main(string[] args)
    {
        string path = "example.txt";

        using (StreamWriter writer = new StreamWriter(path))
        {
            writer.WriteLine("첫 번째 줄");
            writer.WriteLine("두 번째 줄");
        }
        Console.WriteLine("파일 작성 완료");
    }
}
  • using 문: StreamWriter를 사용한 후 자동으로 파일을 닫아주기 위해 using 문을 사용합니다.

4. 텍스트 파일 읽기

File.ReadAllText 메서드

File.ReadAllText는 파일의 모든 내용을 한 번에 읽어 문자열로 반환합니다.

using System;
using System.IO;

class Program
{
    static void Main(string[] args)
    {
        string path = "example.txt";
        string content = File.ReadAllText(path); // 파일 내용 읽기
        Console.WriteLine("파일 내용:");
        Console.WriteLine(content);
    }
}

StreamReader 사용

StreamReader는 파일을 한 줄씩 읽을 때 유용하며, 큰 파일을 처리할 때 성능이 더 좋습니다.

using System;
using System.IO;

class Program
{
    static void Main(string[] args)
    {
        string path = "example.txt";

        using (StreamReader reader = new StreamReader(path))
        {
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                Console.WriteLine(line);
            }
        }
    }
}

5. 파일 존재 여부 확인

파일 입출력 전에 파일이 존재하는지 확인하는 것이 중요할 수 있습니다. 파일이 존재하지 않으면 FileNotFoundException 예외가 발생할 수 있기 때문에, 이를 방지하기 위해 파일 존재 여부를 확인해야 합니다.

파일 존재 여부 확인 예시

string path = "example.txt";

if (File.Exists(path))
{
    Console.WriteLine("파일이 존재합니다.");
}
else
{
    Console.WriteLine("파일이 존재하지 않습니다.");
}

6. 파일에 데이터 추가하기

파일에 데이터를 추가하려면 File.AppendAllText 또는 StreamWriter의 append 모드를 사용할 수 있습니다.

File.AppendAllText 사용

File.AppendAllText("example.txt", "\n추가된 내용");

StreamWriter를 사용하여 파일에 추가

using (StreamWriter writer = new StreamWriter("example.txt", append: true))
{
    writer.WriteLine("추가된 내용");
}

7. 바이너리 파일 입출력 (계속)

FileStream을 이용한 바이너리 파일 읽기

FileStream을 사용하여 바이너리 파일을 읽어 올 수 있습니다.

using System;
using System.IO;

class Program
{
    static void Main(string[] args)
    {
        using (FileStream fs = new FileStream("binaryfile.bin", FileMode.Open))
        {
            byte[] data = new byte[fs.Length];
            fs.Read(data, 0, data.Length);

            Console.WriteLine("바이너리 파일을 읽었습니다:");
            foreach (byte b in data)
            {
                Console.Write($"{b:X2} ");
            }
        }
    }
}

이 코드에서는 binaryfile.bin 파일을 읽고, 그 내용을 출력합니다. Read 메서드를 사용하여 파일의 내용을 바이트 배열로 읽어옵니다.

8. 파일 처리 시 예외 처리

파일을 처리할 때 예외 상황이 발생할 수 있습니다. 예를 들어 파일이 존재하지 않거나, 파일에 대한 접근 권한이 없을 때 예외가 발생할 수 있습니다. 이러한 예외는 try-catch 구문을 사용하여 안전하게 처리할 수 있습니다.

파일 입출력에서의 예외 처리 예시

using System;
using System.IO;

class Program
{
    static void Main(string[] args)
    {
        string path = "nonexistent.txt";

        try
        {
            string content = File.ReadAllText(path);
            Console.WriteLine(content);
        }
        catch (FileNotFoundException ex)
        {
            Console.WriteLine("파일을 찾을 수 없습니다: " + ex.Message);
        }
        catch (UnauthorizedAccessException ex)
        {
            Console.WriteLine("파일에 접근 권한이 없습니다: " + ex.Message);
        }
        catch (Exception ex)
        {
            Console.WriteLine("오류가 발생했습니다: " + ex.Message);
        }
    }
}

이 코드에서는 파일이 존재하지 않거나 접근 권한이 없을 때 예외를 처리합니다.

9. 디렉터리 관리

파일뿐만 아니라 디렉터리(폴더)도 System.IO 네임스페이스에서 관리할 수 있습니다. C#에서 디렉터리를 생성하고 삭제하거나, 특정 디렉터리 내의 파일 목록을 가져오는 기능을 제공받을 수 있습니다.

디렉터리 생성

string folderPath = "NewFolder";

if (!Directory.Exists(folderPath))
{
    Directory.CreateDirectory(folderPath);
    Console.WriteLine("디렉터리가 생성되었습니다.");
}
else
{
    Console.WriteLine("디렉터리가 이미 존재합니다.");
}

디렉터리 내 파일 목록 가져오기

string folderPath = "NewFolder";

if (Directory.Exists(folderPath))
{
    string[] files = Directory.GetFiles(folderPath);

    Console.WriteLine("디렉터리 내 파일 목록:");
    foreach (string file in files)
    {
        Console.WriteLine(file);
    }
}
else
{
    Console.WriteLine("디렉터리가 존재하지 않습니다.");
}

디렉터리 삭제

string folderPath = "NewFolder";

if (Directory.Exists(folderPath))
{
    Directory.Delete(folderPath, true); // true: 하위 디렉터리 및 파일까지 삭제
    Console.WriteLine("디렉터리가 삭제되었습니다.");
}

10. 비동기 파일 입출력

C#에서는 비동기 작업을 쉽게 처리할 수 있도록 async와 await 키워드를 제공합니다. 파일 입출력 작업에서도 비동기 방식으로 데이터를 읽고 쓸 수 있습니다. 비동기 방식은 파일 처리 시간이 오래 걸릴 때 프로그램이 중단되지 않고 다른 작업을 계속 진행할 수 있게 합니다.

비동기 방식으로 파일 쓰기

using System;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        string path = "example_async.txt";
        string content = "This is an async write example.";

        await File.WriteAllTextAsync(path, content); // 비동기 파일 쓰기
        Console.WriteLine("비동기 파일 작성 완료");
    }
}

비동기 방식으로 파일 읽기

using System;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        string path = "example_async.txt";

        if (File.Exists(path))
        {
            string content = await File.ReadAllTextAsync(path); // 비동기 파일 읽기
            Console.WriteLine("파일 내용:");
            Console.WriteLine(content);
        }
        else
        {
            Console.WriteLine("파일이 존재하지 않습니다.");
        }
    }
}

11. 실습 예제: 텍스트 파일에 학생 점수 저장하고 읽어오기

이번 예제에서는 학생들의 점수를 파일에 저장한 후, 해당 파일을 읽어와서 평균 점수를 계산하는 프로그램을 작성해보겠습니다.

학생 점수 저장 예제

using System;
using System.IO;

class Program
{
    static void Main(string[] args)
    {
        string path = "students_scores.txt";

        // 학생들의 점수 작성
        using (StreamWriter writer = new StreamWriter(path))
        {
            writer.WriteLine("Alice, 85");
            writer.WriteLine("Bob, 90");
            writer.WriteLine("Charlie, 78");
        }

        // 파일에서 학생 점수 읽기 및 평균 계산
        using (StreamReader reader = new StreamReader(path))
        {
            string line;
            int totalScore = 0;
            int studentCount = 0;

            while ((line = reader.ReadLine()) != null)
            {
                string[] parts = line.Split(',');
                string name = parts[0].Trim();
                int score = int.Parse(parts[1].Trim());

                Console.WriteLine($"{name}의 점수: {score}");
                totalScore += score;
                studentCount++;
            }

            double average = (double)totalScore / studentCount;
            Console.WriteLine($"평균 점수: {average:F2}");
        }
    }
}

12. 요약

  • 파일 읽기/쓰기: File.WriteAllText, File.ReadAllText, StreamReader, StreamWriter 등을 사용하여 파일의 내용을 쉽게 읽고 쓸 수 있습니다.
  • 파일 추가 쓰기: File.AppendAllText 또는 StreamWriter의 append 모드를 사용하여 파일에 데이터를 추가할 수 있습니다.
  • 바이너리 파일 처리: FileStream을 사용하여 바이너리 데이터를 읽고 쓸 수 있습니다.
  • 디렉터리 관리: Directory 클래스를 사용하여 디렉터리를 생성, 삭제하고 파일 목록을 가져올 수 있습니다.
  • 비동기 입출력: File.WriteAllTextAsync, File.ReadAllTextAsync 등을 사용하여 비동기 방식으로 파일을 처리할 수 있습니다.

다음 편에서는 C# 델리게이트와 이벤트를 다루며, 코드의 동적 흐름을 제어하는 방법을 학습하겠습니다.

728x90