C# 7편: 파일 입출력(IO)로 데이터 읽고 쓰기
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# 델리게이트와 이벤트를 다루며, 코드의 동적 흐름을 제어하는 방법을 학습하겠습니다.