안녕하세요~
오늘은 C# 파일 다루기에 대해 공부해요!
파일 : 컴퓨터 저장 매체에 기록되는 데이터의 묶음
디렉터리 : 파일이 위치하는 주소
.NET에서 파일과 디렉터리를 다루기 위해 제공하는 클래스
| File | 파일의 생성, 복사, 삭제, 이동, 조회를 처리하는 정적 메소드 제공 |
| FileInfo | File과 하는 일을 동일하고 정적 메소드 대신 인스턴스 메소드 제공 |
| Directory | 디렉토리의 생성, 삭제, 이동, 조회를 처리하는 정적 메소드를 제공 |
| DirectoryInfo | Directory 클래스와 하는 일은 동일하지만 정적 메소드 대신 인스턴스 메소드를 제공 |
예제 프로그램 - 디렉토리/파일 정보 조회
class MainApp
{
static void OnWrongPathType(string type)
{
Console.WriteLine($"{type} is wrong type");
return;
}
static void Main(string[] args)
{
if(args.Length == 0)
{
Console.WriteLine("Usage : Touch.exe <Path> [Type:File/Directory]");
return;
}
string path = args[0];
string type = "File";
if(args.Length > 1)
{
type = args[1];
}
if(File.Exists(path) || Directory.Exists(path))
{
if(type == "File")
{
File.SetLastWriteTime(path, DateTime.Now);
}
else if(type == "Directory")
{
Directory.SetLastWriteTime(path, DateTime.Now);
}
else
{
OnWrongPathType(path);
return;
}
Console.WriteLine($"Updated {path} {type}");
}
else
{
if(type == "File")
{
File.Create(path).Close();
}
else if(type == "Directory")
{
Directory.CreateDirectory(path);
}
else
{
OnWrongPathType(path);
return;
}
Console.WriteLine($"Created {path} {type}");
}
}
}
파일을 읽고 쓰기 위해 알아야 할 것들
Stream : 데이터가 흐르는 통로
순차 접근(Sequential Access) : 처음부터 끝까지 순서대로 읽고 쓰는 것
임의 접근(Random Access) : 임의의 주소에 있는 데이터에 바로 접근하는 것
System.IO.Stream 클래스
stream 클래스
입력 스트림, 출력 스트림 역할을 모두 할 수 있고
파일을 읽고 쓰는 방식은 순차접근, 임의접근 방식 모두 지원
FileStream 쓰기/읽기
Stream클래스의 아래 쓰기 관련 두가지 메소드를 오버라이딩합니다.
public override void Write(byte[] buffer, int offset, int count) // 쓸 데이터가 담긴 byte 배열, byte 배열 내 시작 오프셋, 기록할 데이터의 총 길이(단위는 바이트)
public override void WriteByte(byte value)
Stream클래스의 아래 읽기 관련 두가지 메소드를 오버라이딩합니다.
public override int Read(byte[] buffer, int offset, int count)// 읽을 데이터가 담긴 byte 배열, byte 배열 내 시작 오프셋, 읽을 데이터의 최대 바이트 수
public override int ReadByte()
매개 변수가 모두 byte 형태에요
따라서 BitConverter 클래스를 이용해야해요
BitConverter
임의 형식의 데이터를 byte의 배열로 변환해주기도 하지만,
byte의 배열에 담겨 있는 데이터를
다시 임의 형식으로 변환해줄 수 있어요.
예제코드 추가할게요.
class MainApp
{
static void Main(string[] args)
{
long someValue = 0x123456789ABCDEF0;
Console.WriteLine("{0, -1} : 0x{1:X16}", "Original Data", someValue);
Stream outStream = new FileStream("a.dat", FileMode.Create);
byte[] wBytes = BitConverter.GetBytes(someValue); //쓸 데이터를 바이트로 변환
Console.Write("{0, -13}", "Byte Array");
foreach(byte b in wBytes)
{
Console.Write("{0:X2} ", b);
}
Console.WriteLine();
outStream.Write(wBytes, 0, wBytes.Length);
outStream.Close();
//읽기
Stream inStream = new FileStream("a.dat", FileMode.Open);
byte[] rBytes = new byte[8];
int i = 0;
while(inStream.Position < inStream.Length)
{
rBytes[i++] = (byte)inStream.ReadByte();
}
long readValue = BitConverter.ToInt64(rBytes, 0);
Console.WriteLine("{0,-13} : 0x{1:X16} ", "Read Data", readValue);
inStream.Close();
}
}
여러개의 데이터를 기록하기
Stream 클래스에는 Position이라는 프로퍼티가 있어요.
현재 스트림의 읽는 위치 또는 쓰는 위치를 나타내요
여러개의 데이터를 기록하는 일은
Write()나 WriteByte() 메소드를
차례차례 호출하는 것으로 충분해요.(순차접근)
Seek() 메소드를 호출하거나 Position 프로퍼티에 직접 원하는 값을 대입하여
지정한 위치로 점프해 읽기/쓰기를 위한 준비도 할 수 있어요.(임의접근)
예제코드 추가할게요.
class MainApp
{
static void Main(string[] args)
{
Stream outStream = new FileStream("b.dat", FileMode.Create);
Console.WriteLine($"Position : {outStream.Position}");
outStream.WriteByte(0x01);
Console.WriteLine($"Position : {outStream.Position}");
outStream.WriteByte(0x02);
Console.WriteLine($"Position : {outStream.Position}");
outStream.WriteByte(0x03);
Console.WriteLine($"Position : {outStream.Position}");
outStream.Seek(5, SeekOrigin.Current);
Console.WriteLine($"Position : {outStream.Position}");
outStream.WriteByte(0x04);
Console.WriteLine($"Position : {outStream.Position}");
outStream.Close();
FileStream inStream = new FileStream("b.dat", FileMode.Open);
byte[] rBytes = new byte[1024];
int i = 0;
while(inStream.Position < inStream.Length)
{
rBytes[i++] = (byte)inStream.ReadByte();
}
long readInfo = BitConverter.ToInt32(rBytes);
Console.WriteLine($"{readInfo}");
}
}
using 선언을 통한 파일 닫기
파일을 열어서 사용한 후
자원을 해제하지 않는 실수가 자주 일어나요
C#은 이러한 실수를 줄일 수 있는 using을 제공해요
아래처럼 using문을 사용하면
코드 블록의 마지막에서 Dispose() 메소드가 호출되어요
Stream.Close() 메소드는
IDispoable 인터페이스를 상속받은 Dispose() 메소드를 호출해요
따라서 아래 코드와 같이 using을 사용하면
Stream.Close()를 호출한 것과 동일한 효과에요
using (Stream inStream = new FS("a.dat", FileMode.Open))
{
byte[] rBytes = new byte[8];
int i = 0;
while(inStream.Position < inStream.Length)
{
rBytes[i++] = (byte)inStream.ReadByte();
}
long readValue = BitConverter.ToInt64(rBytes, 0);
Console.WriteLine("{0,-13} : 0x{1:X16} ", "Read Data", readValue);
}
이진 데이터 처리
FileStream 클래스는 파일 처리를 위한 모든 것을 가지지만
데이터를 저장하거나 읽을 때 byte 나 byte 배열 형식으로
변환해야한다는 불편함이 있어요
이런 불편함을 해결하기 위해
.NET은 BinaryWriter, BinaryReader 클래스를 제공해요
BinaryWriter는 스트림에 이진 데이터를 기록하고
BinaryReader는 스트림으로부터 이진 데이터를 읽어들이는 역할을 한답니다.
BinaryWriter와 BinaryReader 클래스를 생성할때는
FileStream 인스턴스를 넘겨주어야해요.
BinaryWriter와 BinaryReader 클래스의
Write(), Read() 메소드는
C#이 제공하는 모든 기본 데이터 형식에 대해
오버로딩하고 있어서 훨씬 간편하게 사용할 수 있어요.
아래와 같이 사용할 수 있답니다.
class MainApp
{
static void Main(string[] args)
{
using (BinaryWriter bw = new BinaryWriter(new FileStream("c.dat", FileMode.Create)))
{
bw.Write(int.MaxValue);
bw.Write("Good Morning!");
bw.Write(uint.MaxValue);
bw.Write("안녕하세요!");
bw.Write(double.MaxValue);
string str = string.Empty;
for(int i = 0; i < 256; i++)
{
str += 'a';
}
bw.Write(str);
}
using (BinaryReader br = new BinaryReader(new FileStream("c.dat", FileMode.Open)))
{
Console.WriteLine($"File Size : {br.BaseStream.Length} bytes");
Console.WriteLine(br.ReadInt32());
Console.WriteLine(br.ReadString());
Console.WriteLine(br.ReadUInt32());
Console.WriteLine(br.ReadString());
Console.WriteLine(br.ReadDouble());
Console.WriteLine(br.ReadString());
}
}
}
텍스트 파일 처리
.NET은 이진 데이터뿐만 아니라
텍스트 파일을 쓰고 읽을 수 있는
StreamWriter와 StreamReader 클래스를 제공해요.
BinaryWriter, BinaryReader와 사용법이 유사해서
바로 예제 코드 추가할게요.
class MainApp
{
static void Main(string[] args)
{
using (StreamWriter sw = new StreamWriter(new FileStream("a.txt", FileMode.Create)))
{
sw.WriteLine(int.MaxValue);
sw.WriteLine("Good Morning!");
sw.WriteLine(uint.MaxValue);
sw.WriteLine("안녕하세요!");
sw.WriteLine(double.MaxValue);
}
using (StreamReader sr = new StreamReader(new FileStream("a.txt", FileMode.Open)))
{
Console.WriteLine($"File Size : {sr.BaseStream.Length} bytes");
while(sr.EndOfStream == false)
{
Console.WriteLine(sr.ReadLine());
}
}
}
}'C#' 카테고리의 다른 글
| [C#] Thread (1) | 2024.10.18 |
|---|---|
| [C#] 직렬화 역직렬화 (0) | 2024.10.16 |
| [C#] Dynamic 형식 (2) | 2024.10.15 |
| [C#] 리플렉션과 애트리뷰트 (1) | 2024.10.14 |
| [C#] LINQ (1) | 2024.10.13 |