System.IO.FileStream
FileStream은 파일을 읽고 쓰기 위한 BCL(Base Class Library)의 가장 기본적인 스트림 타입이다.
FileStream은 Stream 클래스를 상속받으며, 파일을 바이트 단위로 읽고 쓰는 기능을 제공한다.
MemoryStream 역시 같은 Stream 클래스를 상속받기 때문에, 두 클래스는 전체적인 사용 방식이 매우 유사하다.
MemoryStream과 FileStream의 차이
두 클래스의 가장 큰 차이는 데이터를 대상으로 하는 위치이다.
- MemoryStream
- 메모리에 존재하는 **바이트 배열(byte[])**을 대상으로 읽기/쓰기 작업을 수행한다.
- 디스크 접근이 없기 때문에 속도가 빠르다.
- FileStream
- 디스크에 존재하는 파일을 대상으로 읽기/쓰기 작업을 수행한다.
- 실제 파일 시스템과 상호작용한다.
구조 관계
├─ MemoryStream
└─ FileStream
Stream은 데이터 흐름을 다루기 위한 추상 클래스이며
MemoryStream, FileStream, NetworkStream 등 다양한 스트림 클래스들이 이를 상속받아 구현된다.
using (FileStream fileStream = new FileStream("test.log", FileMode.Create))
{
StreamWriter streamWriter = new StreamWriter(fileStream, System.Text.Encoding.UTF8);
streamWriter.WriteLine("Hello World");
streamWriter.WriteLine("Andrson");
streamWriter.WriteLine(32000);
streamWriter.Flush();
}

BinaryWriter를 사용한 FileStream
using (FileStream fileStream = new FileStream("test.log", FileMode.Create))
{
BinaryWriter binaryWriter = new BinaryWriter(fileStream);
binaryWriter.Write("Hello World" + Environment.NewLine);
binaryWriter.Write("Andrson" + Environment.NewLine);
binaryWriter.Write(32000);
binaryWriter.Flush();
}

32000의 값이 공백과 } 문자로 출력되는 이유
BinaryWriter는 데이터를 문자열 형태가 아니라 이진(Binary) 형태로 저장하기 때문이다.
"32000" 문자열로 저장되는 것이 아니라 정수 값의 바이트 형태로 저장된다.
00 7D 00 00 텍스트 편집기는 이 데이터를 문자(문자 인코딩)로 해석하려고 하기 때문에
}
같은 의미 없는 문자로 보이게 된다.
즉, BinaryWriter → 바이트 데이터 기록 / 텍스트 에디터 → 문자로 해석 이 차이 때문에 사람이 보기에는 이상한 문자로 나타난다.
FileStream을 이용해 파일을 읽고 쓸 때 사람이 읽을 수 있게 하려면 StreamWriter를 사용하고 가독성을 무시하고 효율적으로 데이터를 기록하려면 BinaryWriter를 사용하면 된다.
FileMode
| 열거형 값 | 설명 |
| CreateNew | 파일을 새롭게 생성한다. 같은 이름의 파일이 있다면 IOException 예외가 발생한다. |
| Create | 파일을 생성한다. 같은 이름의 파일이 있다면 기존 데이터가 모두 삭제된다. |
| Open | 이미 있는 파일을 연다. 만약 지정된 이름의 파일이 존재하지 않는다면 FileNotFoundException 예외가 발생한다. |
| OpenOrCreate | 같은 이름의 파일이 이미 있다면, 파일을 열고, 없다면 생성한다. |
| Truncate | 이미 있는 파일을 열고 기존 데이터는 모두 삭제한다. 같은 이름의 파일이 존재하지 않는다면 FileNotFoundException 예외가 발생한다. |
| Append | 파일을 무조건 연다. 같은 이름의 파일이 있다면 FileStream의 Position 값을 마지막 위치로 자동으로 이동시킨다. 같은 이름의 파일이 없다면 새롭게 생성한다. |
FileAccess
| 열거형 값 | 설명 |
| Read | 파일을 읽기 목적으로 연다 |
| Write | 파일을 쓰기 목적으로 연다 |
| ReadWrite | 파일을 읽기 및 쓰기 목적으로 연다. 이 모드는 FileAccess.Read | FileAccess.Write로 지정한 것과 같다. |
FileSharing
| 열거형 값 | 설명 |
| None | 다른 프로세스가 해당 파일을 다시 여는 것을 허용하지 않는다. |
| Read | 다른 프로세스가 읽기(Read) 목적으로 파일을 여는 것을 허용한다. |
| Write | 다른 프로세스가 쓰기(Write) 목적으로 파일을 여는 것을 허용한다. |
| ReadWrite | 다른 프로세스가 읽기 또는 쓰기 목적으로 파일을 여는 것을 허용한다. |
자주 사용되는 옵션 조합
| 옵션 조합 | 설명 |
| FileMode.Append | 로깅(logging) 목적의 파일 쓰기를 하는 경우 사용한다. (FileMode.Append인 경우 FileAccess는 Write만 허용한다. 또한 FileShare의 기본 값은 Read이므로 굳이 지정할 필요가 없다.) |
| FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None |
재사용되는 전용 데이터를 입/출력하는 목적인 경우 사용한다. |
| FileMode.Create, FileMode.ReadWrite, FileMode.None |
임시로 사용되는 데이터를 입/출력하는 목적인 경우 사용한다. |
FileMode.Append (로그 파일 작성)
전용 데이터 파일을 읽고 쓰는 경우 사용한다.
옵션 의미
- OpenOrCreate → 파일이 없으면 생성
- ReadWrite → 읽기 / 쓰기 모두 가능
- FileShare.None → 다른 프로그램이 접근 불가
using System;
using System.IO;
class Program
{
static void Main()
{
using (FileStream fs = new FileStream("app.log", FileMode.Append))
using (StreamWriter writer = new StreamWriter(fs))
{
writer.WriteLine($"[{DateTime.Now}] 프로그램 실행");
}
}
}
FileMode.OpenOrCreate + ReadWrite + FileShare.None
using System;
using System.IO;
using System.Text;
class Program
{
static void Main()
{
using (FileStream fs = new FileStream(
"save.dat",
FileMode.OpenOrCreate,
FileAccess.ReadWrite,
FileShare.None))
{
byte[] data = Encoding.UTF8.GetBytes("PlayerLevel:10\n");
fs.Write(data, 0, data.Length);
fs.Position = 0;
byte[] readBuffer = new byte[fs.Length];
fs.Read(readBuffer, 0, readBuffer.Length);
Console.WriteLine(Encoding.UTF8.GetString(readBuffer));
}
}
}
FileMode.Create + ReadWrite + FileShare.None
임시 파일이나 새 데이터를 완전히 다시 생성해야 하는 경우 사용한다.
옵션 의미
- Create → 기존 파일이 있으면 삭제 후 새로 생성
- ReadWrite → 읽기 / 쓰기 가능
- FileShare.None → 다른 프로그램 접근 차단
using System;
using System.IO;
using System.Text;
class Program
{
static void Main()
{
using (FileStream fs = new FileStream(
"temp.dat",
FileMode.Create,
FileAccess.ReadWrite,
FileShare.None))
{
byte[] data = Encoding.UTF8.GetBytes("Temporary Data");
fs.Write(data, 0, data.Length);
fs.Position = 0;
byte[] buffer = new byte[fs.Length];
fs.Read(buffer, 0, buffer.Length);
Console.WriteLine(Encoding.UTF8.GetString(buffer));
}
}
}
추가로 FileShare.None일 경우 파일을 메모장으로 열려고 시도하면 열 수 없다는 오류가 발생하는데, 이는 메모장이 파일을 로드할 때 FileAccess.Read 모드로 열기 때문에 공유를 허락하지 않는 FileShare.None과 충돌이 발생하기 때문이다.
기본 경로 설정 (CurrentDirectory)
파일을 열 때 경로를 지정하지 않으면
프로그램은 현재 작업 디렉토리(Current Directory) 를 기준으로 파일을 찾는다.
이 기본 경로는 다음 속성으로 확인할 수 있다.
Environment.CurrentDirectory
예를 들어 new FileStream("test.txt", FileMode.Open);
이 코드는 실제로 다음 경로에서 파일을 찾는다.
CurrentDirectory 변경
현재 작업 디렉토리는 코드에서 변경할 수도 있다.
주의사항
CurrentDirectory를 변경할 때는 반드시 존재하는 디렉토리 경로를 지정해야 한다.
존재하지 않는 경로를 지정하면 다음 예외가 발생한다.
using System;
using System.IO;
class Program
{
static void Main()
{
// 현재 기본 경로 출력
Console.WriteLine("현재 경로: " + Environment.CurrentDirectory);
// 기본 경로 변경
Environment.CurrentDirectory = @"C:\temp";
// 변경된 경로 확인
Console.WriteLine("변경된 경로: " + Environment.CurrentDirectory);
// 파일 생성 (C:\temp\test.txt)
using (FileStream fs = new FileStream("test.txt", FileMode.Create))
{
Console.WriteLine("test.txt 파일 생성 완료");
}
}
}
정리
- 경로를 지정하지 않으면 Environment.CurrentDirectory 기준으로 파일을 찾는다.
- CurrentDirectory는 코드에서 변경할 수 있다.
- 존재하지 않는 디렉토리를 지정하면 DirectoryNotFoundException이 발생한다.
System.IO.File / System.IO.FileInfo
File 타입은 파일을 다루기 위해 자주 사용되는 기능들을 제공하는 정적(static) 클래스이다.
따라서 File 클래스에서 제공하는 모든 메서드는 정적 메서드(static method) 로 구현되어 있으며, 객체를 생성하지 않고 클래스 이름을 통해 직접 호출할 수 있다.
File 타입의 정적 메서드
| 정적 메서드 | 설명 |
| Copy | 파일을 복사한다. |
| Exist | 파일이 존재하는지 여부를 true/false로 반환한다. |
| Move | 파일을 이동한다 |
| ReadAllBytes | 파일의 모든 내용을 읽어 byte 배열로 반환한다. |
| ReadAllLines | 텍스트 파일의 모든 내용을 string 배열로 반환한다. 한 줄당 문자열 하나로 대응된다 |
| ReadAllText | 텍스트 파일의 모든 내용을 읽어 string 객체로 반환한다. |
| WriteAllBytes | 지정된 byte 배열을 모두 파일에 쓴다 |
| WriteAllLines | 지정된 string 배열의 모든 내용을 개행 문자와 함께 파일에 쓴다. |
| WriteAllText | 지정된 string 인자의 값을 모두 파일에 쓴다. |
File.Copy
File.Copy 메서드는 파일을 지정한 경로로 복사할 때 사용된다.
기본적으로 복사 대상 위치에 이미 같은 이름의 파일이 존재할 경우 IOException 예외가 발생한다.
//경로가 지정되지 않으면 Environment.CurrentDirectory가 기본 경로로 사용된다.
//대상 폴더에 파일이 없다면
File.Copy("test.log", "test.dat");
//대상 폴더에 파일이 있고 덮어 쓸 의도라면
File.Copy("test.log",. "test.dat", true);
3번째 인자의 boolean 값이 true인 경우 대상 경로에 이미 파일이 있어도 덮어쓰기를 한다.
File.Move
File.Move 메서드는 파일을 다른 위치로 이동할 때 사용된다.
이 메서드는 원본 파일 경로와 대상 파일 경로를 나타내는 두 개의 인자를 가진다.
파일 이름 변경 (Rename)
File.Move는 파일을 다른 위치로 이동하는 목적뿐만 아니라 파일 이름을 변경(rename)하는 용도로도 사용할 수 있다.
특히 같은 폴더 내에서 경로만 바꾸면 파일 이름이 변경된다.
//폴더가 동일하다면 파일명 변경
File.Move("test.log", "test.dat");
//폴더가 다르다면 파일 이동
File.Move("test.log", "c:\\temp\\test.dat");
File.Move 사용 시 주의사항
File.Move는 대상 경로에 이미 같은 이름의 파일이 존재할 경우 IOException 예외가 발생한다.
File.Move는 덮어쓰기 옵션이 없다
File.Copy와 달리 File.Move는 덮어쓰기(overwrite) 옵션을 제공하지 않는다.
따라서 대상 경로에 같은 이름의 파일이 존재하는지 먼저 확인한 후 필요하다면 기존 파일을 삭제해야 한다.
Move메서드의 덮어쓰기
string target = "c:\\temp\test.dat";
if(File.Exists(target) == true)
{
File.Delete(target);
}
File.Move("test.log", target);
파일의 내용을 한번에 읽고 쓰는 예시(WriteAllText, ReadAllText)

File.Replace
File.Replace 메서드는 기존 파일을 다른 파일로 교체하면서 동시에 백업 파일을 생성하는 기능을 제공한다.
이 메서드는 설정 파일이나 중요한 데이터 파일을 안전하게 업데이트할 때 자주 사용된다.
File.Replace(sourceFileName, destinationFileName, destinationBackupFileName);
매개변수설명
| sourceFileName | 교체할 파일 |
| destinationFileName | 교체될 대상 파일 |
| destinationBackupFileName | 기존 대상 파일을 백업할 파일 |
동작 과정
File.Replace는 내부적으로 다음 순서로 동작한다.
- destination 파일을 backup 파일로 복사
- source 파일로 destination 파일을 교체
- source 파일 삭제
결과적으로 source → destination | destination → backup 형태가 된다.
newConfig.txt 파일로 config.txt 파일을 교체하면서 기존 파일을 backup.txt로 백업하는 예시
using System;
using System.IO;
class Program
{
static void Main()
{
File.Replace(
"newConfig.txt",
"config.txt",
"backup.txt"
);
Console.WriteLine("파일 교체 완료");
}
}
파일 교체 과정에서 문제가 발생해도 기존 파일을 백업으로 복구할 수 있기 때문에 안정성이 높다.
File.Replace 메서드 (System.IO)
지정된 파일의 내용을 다른 파일의 내용으로 바꾸고 원래 파일을 삭제하고 대체된 파일의 백업을 만듭니다.
learn.microsoft.com
FileInfo
FileInfo 타입은 File 클래스에서 제공하는 파일 조작 기능을 인스턴스 멤버 형태로 제공하는 클래스이다.
File 클래스는 정적(static) 클래스이기 때문에 객체 생성 없이 사용할 수 있지만, FileInfo는 객체를 생성한 뒤 해당 파일에 대한 작업을 수행한다는 차이점이 있다.
FileInfo로 File.Move를 구현한 코드
FileInfo source = new FileInfo("test.log");
FileInfo target = new FileInfo("c:\\temp\\test.dat");
if(target.Exist == true)
{
target.Delete();
}
source.MoveTo(target.FullName); //이때 폴더가 같다면 파일 이름 변경(rename) 용도로도 사용할 수 있다.
File.Move 또는 FileInfo.MoveTo 메서드는 대상 경로에 따라 동작이 달라진다.
다른 폴더를 지정하면 파일이 이동(move) 되고, 같은 폴더에서 파일 이름만 변경하면 파일 이름 변경(rename) 으로 동작한다.
System.IO.Directory / System.IO.DirectoryInfo
Directory 타입과 DirectoryInfo 타입의 관계는 File과 FileInfo의 관계와 거의 동일하다.
Directory 클래스는 정적(static) 클래스로 구성되어 있으며,
디렉토리 생성, 삭제, 이동 등과 같은 파일 시스템 작업을 정적 메서드 형태로 제공한다.
반면 DirectoryInfo는 인스턴스 클래스로,
Directory 클래스에서 제공하는 일부 기능을 객체 기반의 인스턴스 멤버 형태로 제공한다.
즉, 두 클래스의 차이는 다음과 같다.
Directory → 정적 클래스
DirectoryInfo → 인스턴스 클래스
Directory는 간단한 디렉토리 작업을 수행할 때 사용되며, DirectoryInfo는 특정 디렉토리를 객체로 다루면서 작업할 때 사용된다.
Directory 타입의 정적 메서드
| 정적 메서드 | 설명 |
| CreateDirectory | 디렉토리를 생성한다. 이미 디렉토리가 존재한다면 아무런작업도 하지 않는다. |
| Delete | 디렉토리를 삭제한다. 존재하지 않는 디렉토리를 삭제하는 경우 DirectoryNotFoundException 예외가 발생한다 |
| Exists | 디렉토리가 존재하는지 여부를 true/false로 반환한다. |
| GetDirectories | 지정된 경로의 하위 디렉토리 목록을 문자열 배열로 반환한다. |
| GetFiles | 지정된 경로에 있는 파일을 문자열 배열로 반환한다. |
| GetLogicalDrives | 시스템에 설치된 디스크의 드라이브 문자 목록을 string 배열로 반환한다. |
| Move | 디렉토리를 이동한다. |
Directory 타입을 이용해 '윈도우 탐색기'와 비슷한 유사 프로그램 만들기
- 컴퓨터의 모든 디스크 드라이브를 나열한다.
- 특정 폴더의 파일 목록을 나열한다.
- 특정 폴더의 디렉토리 목록을 나열한다.
- 특정 폴더 및 그 폴더의 모든 하위 폴더를 검색해서 파일을 찾는다.
using System;
using System.Collections;
using System.Collections.Generic;
namespace BCL_Study
{
internal class StrStudy
{
static void Main(string[] args)
{
//컴퓨터의 모든 디스크 드라이브 나열
foreach (string txt in Directory.GetLogicalDrives())
{
Console.WriteLine(txt);
}
Console.WriteLine();
//특정 폴더의 파일 목록을 나열
string targetpath = @"C:\Windows\Microsoft.NET\Framework";
foreach(string txt in Directory.GetFiles(targetpath))
{
Console.WriteLine(txt);
}
Console.WriteLine() ;
//특정 폴더의 디렉토리 목록을 나열
foreach(string txt in Directory.GetDirectories(targetpath))
{
Console.WriteLine(txt);
}
Console.WriteLine();
//특정 폴더 및 그 폴더의 모든 하위 폴더를 검색해서 파일을 찾는다.
foreach(string txt in Directory.GetFiles(targetpath, "*.exe", SearchOption.AllDirectories))
{
Console.WriteLine(txt);
}
}
}
}
Directory.GetFiles와 와일드카드 문자
Directory.GetFiles 메서드는 특정 폴더에 있는 파일 목록을 가져올 때 사용된다.
이때 두 번째 인자로 검색 패턴(search pattern) 을 지정할 수 있다.
여기서 "*.exe"에 사용된 * 문자는 와일드카드(wildcard) 문자이다.
다른 와일드 카드 문자인 ?는 어떤 하나의 문자를 나타낸다.
와일드 카드의 예시
| 문자열 | 의미 | 사례 |
| net*.* | 확장자는 상관없고, 파일명이 'net'으로 시작하는 모든 파일 | netframework.dll net_.dat nettest.exe |
| net?.* | 확장자는 상관없고, 'net'으로 시작하는 총 4글자로 된 파일명을 가진 모든 파일 | net1.dll net_.exe netp.dat |
| ???.dll | 확장자가 DLL이고, 파일명이 3글자인 모든 파일 | tes.dll fra.dll kor.dll |
| *. | 확장자가 없는 모든 파일 | netfx |
| *script." | 확장자는 상관없고, 파일명이 script로 끝나는 모든 파일 | Microsoft.JScript.dll VBScript.tlb |




System.IO.Path
Path타입은 파일 경로와 관련해서 유용한 정적 메서드를 제공한다.
| 정적 메서드 | 설명 |
| ChangeExtension | 첫 번째 인자로 주어진 경로에서 확장자 부분을 두 번째 인자로 전달된 문자열로 바꿔준다. |
| Combine | 전달된 문자열 인자를 모두 합쳐서 하나의 경로로 만든다 |
| GetDirectoryName | 전달된 문자열에서 파일 이름이 포함된 경우 그 파일의 부모 디렉토리 이름을 반환한다. 반면 디렉토리 이름이 포함된 경우 그 부모 디렉토리 이름을 반환한다. |
| GetExtension | 전달된 문자열의 확장자를 반환한다. |
| GetFileName | 전달된 문자열의 파일명을 반환한다. |
| GetFilenameWithoutExtension | 전달된 문자열의 파일명을 확장자를 제외시켜 반환한다. |
| GetFullPath | 전달된 문자열의 파일명을 제외한 경로를 반환한다. |
| GetInvalidFileNameChars | 파일 이름으로 부적절한 문자의 배열을 반환한다. |
| GetInvalidPathChars | 경로 이름으로 부적절한 문자의 배열을 반환한다. |
| GetPathRoot | 전달된 문자열의 루트 드라이브 문자열을 반환한다. |
| GetRandomFilename | 임의의 파일명을 반환한다. |
| GetTempFileName | 윈도우의 임시 폴더 경로에 임의의 파일을 생성하고, 그 경로를 반환한다. |
| GetTempPath | 윈도우의 임시 폴더 경로를 반환한다. |
Path.Combine을 통해 복잡한 경로 연결 문제를 해결해줄 수 있다.
string filePath = Path.Combine(@"C:\temp", "test", "myfile.dat");
Console.WriteLine(filePath); //출력 결과: C:\temp\test\myfile.dat
Combine 메서드는 params 유형의 string 배열 인자를 취하기 때문에 몇 개의 인자를 전달해도 상관 없다.
GetInvalidFileNameChars나 GetInvalidPathChars 메서드는 생성해야 할 파일이나 디렉토리 이름이 올바른지 확인하는데 도움을 준다.
폴더 이름 유효성 검사
직접 만든 탐색기 프로그램에서 새 폴더를 생성하기 위해 사용자에게 폴더 이름을 입력받는다고 가정해 보자.
만약 사용자가 폴더 이름으로 my<new와 같은 문자열을 입력했다면, 폴더 이름에 사용할 수 없는 < 문자가 포함되어 있기 때문에 Directory.CreateDirectory를 실행할 때 예외가 발생한다.
이처럼 파일이나 폴더 이름에 사용할 수 없는 문자가 포함된 경우에는 디렉터리를 생성할 수 없다.
따라서 이런 경우에는 예외가 발생한 뒤에 처리하기보다는, 디렉터리를 생성하기 전에 입력값을 미리 검사하여 사용자에게 올바른 폴더 이름을 입력하도록 안내하는 것이 좋다.
string newDirName = "my<new"; //폴더명에 < 문자 허용X
int include = newDirName.IndexOfAny(Path.GetInvalidPathChars());
if(include != -1)
{
Console.WriteLine("폴더명에 적절하지 않은 문자가 있음");
}
임시 폴더 (Temporary Folder)
Windows에는 임시 파일을 저장하기 위한 임시 폴더(Temporary Folder) 가 존재한다.
프로그램에서 작업 중 일시적으로 사용하는 파일을 생성할 때 이 폴더를 사용하는 것이 일반적이다.
.NET에서는 Path.GetTempPath() 메서드를 사용하여 현재 시스템의 임시 폴더 경로를 얻을 수 있다.
임시 파일 생성
임시 폴더에 임시 파일을 생성하는 메서드도 제공된다.
Path.GetTempFileName() 메서드는 임시 폴더에 크기가 0인 파일을 생성하고 그 파일의 전체 경로를 반환한다.
임시 파일 이름만 필요한 경우
만약 실제 파일을 생성하지 않고 임시 파일 이름만 필요하다면 Path.GetRandomFileName() 메서드를 사용할 수 있다.
이 메서드는 중복될 가능성이 매우 낮은 임의의 파일 이름을 생성하여 반환한다.
//크기가 0인 임시 파일을 생성하고 그 경로를 반환한다.
string createdTempFilePath = path.GetTempFileName();
Console.WriteLine(createTempFilePath);
//임시 파일을 생성하지 않고 중복될 확률이 낮은 임시 파일 경로를 구한다.
string tempFilePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
Console.WriteLine(tempFilePath);

Reference
시작하세요! C# 12 프로그래밍 기본 문법부터 실전 예제까지
'C#' 카테고리의 다른 글
| C# BCL(Base Class Library) - 스레딩2 (0) | 2026.03.15 |
|---|---|
| C# BCL(Base Class Library) - 스레딩1 (0) | 2026.03.15 |
| C# BCL(Base Class Library) - 컬렉션 (0) | 2026.03.09 |
| C# BCL(Base Class Library) - 직렬화/역직렬화 (0) | 2026.03.07 |
| C# BCL(Base Class Library) - 문자열 처리 (0) | 2026.03.07 |