Systme.IO 네임스페이스에는 파일과 데이터 스트림을 읽고 쓸 수 있게 해주는 수많은 클래스들이 제공됩니다.(참고 MSDN)
이 중에서 먼저 파일, 디렉터리 정보를 다루는 방법부터 알아봅니다.
File : 단일 파일에 대한 만들기, 복사, 삭제, 이동 및 열기를 위한 정적 메서드를 제공하고 FileStream 개체 만들기를 지원합니다.
FileInfo : FIle과 유사하나 정적메소드가 아닌 인스턴스 메소드를 제공합니다.
Directory : 디렉터리와 하위 디렉터리에서 만들기, 이동 및 열거를 위한 정적 메서드를 제공합니다.
DirectoryInfo: 역시 Directory와 유사하나 정적이 아닌 인스턴스 메소드를 제공합니다.
File and FileInfo
모든 메소드와 속성을 다 살펴볼 수는 없고 대표적인 메소드와 속성을 살펴봅니다.
(참고 File MSDN, FileInfo MSDN)
*MSDN은 정말 방대합니다. 도저히 다 알고 있을 수도 없으므로 작업을 하기 전에 늘 참고하여 내가 원하는 기능을 가진 클래스가 있는가를 알아보는 것이 중요하겠습니다.
기능 | File | FileInfo |
생성 | Create() | Create() |
복사 | Copy() | CopyTo() |
삭제 | Delete() | Delete() |
아동 | Move() | MoveTo() |
존재여부확인 | Exists() | Exists |
속성조회 | GetAttributes() | Attributes |
File은 static이므로 메소드만이 존재하고, 인스턴스를 만드는 FileInfo에는 메소드와 속성이 존재합니다. 위의 표에 ()가 없는 것은 속성입니다.
각각 코딩 스타일을 살펴봅니다.
생성
File :
FileStream fs = File.Create("a.dat");
FileInfo:
FileInfo file = new FileInfo("a.dat");
FileStream fs = file.Create();
복사
File:
File.Copy("a.dat", "b.dat");
FileInfo:
FileInfo f1 = new FileInfo("a.dat");
FileInfo f2 = f1.CopyTo("b.dat");
삭제
File:
File.Delete("a.dat");
Fileinfo:
FileInfo file = new Fileinfo("a.dat");
file.Delete();
이동
File:
File.Move("a.dat", "b,dat");
FileInfo:
FileInfo file = new FileInfo("a.dat");
file.MoveTo("b.dat");
File.Move("a.dat", "b,dat");
FileInfo:
FileInfo file = new FileInfo("a.dat");
file.MoveTo("b.dat");
존재 여부 확인
File:
If (File.Exists("a.dat"))
// ....
FileInfo:
FileInfo file = new FIleInfo("a.dat");
if (file.Exists)
// ...
속성 조회
File:
Console.WriteLine(File.GetAttributes("a.dat"));
FileInfo:
FileInfo file = new FIleInfo("a.dat");
Console.WriteLine(file.Attributes);
Directory and DirectoryInfo
이번에는 Directory와 DirectoryInfo의 대표적 메소드, 속성을 알아봅니다.
기능 | Directory | Directoryinfo |
생성 | CreateDirectory() | Create() |
삭제 | Delete() | Delete() |
이동 | Move() | MoveTo() |
존재여부확인 | Exists() | Exists |
속성조회 | GetAttributes() | Attributes |
하위디렉토리 조회 | GetDirectories() | GetDirectories() |
하위파일조회 | GetFiles() | GetFiles() |
생성
Directory:
DirectoryInfo dir = Directory.CreateDirectory("a");
DirectoryInfo:
DirectoryInfo dir = new Directoryinfo("a");
dir.Create();
삭제
Directory:
Directory.Delete("a");
DirectoryInfo:
DirectoryInfo dir = new Directoryinfo("a");
dir.Delete();
이동
Directory:
Directory.Move("a", "b");
DirectoryInfo:
DirectoryInfo dir = new Directoryinfo("a");
dir.MoveTo("b");
존재 여부 확인
Directory:
if (Directory.Exists("a"));
// ...
DirectoryInfo:
DirectoryInfo dir = new Directoryinfo("a");
if (dir.Exists);
// ...
속성 조회
Directory:
Console.WriteLine(Directory.GetAttributes("a"));
DirectoryInfo:
DirectoryInfo dir = new Directoryinfo("a");
Console.WriteLine(dir.Attributes);
하위 디렉터리 조회
Directory:
string[] dirs = Directory.GetDirectories("a");
DirectoryInfo:
DirectoryInfo dir = new Directoryinfo("a");
DirectoryInfo[] dirs = dir.GetDirectories();
하위 파일 조회
Directory:
string[] files = Directory.GetFiles("a");
DirectoryInfo:
DirectoryInfo dir = new Directoryinfo("a");
FileInfo[] files = dir.GetFiles();
Stream
스트림은 말 그대로 데이터의 흐름입니다. 저장소와 저장소 사이에 파이프라인을 붙이는 것이라고 봐도 무방할 것 같습니다. 예를 들어 메모리에서 하드디스크로 데이터를 옮길 때에 이 둘 사이에 스트림을 형성합니다. 그리고 메모리의 데이터를 바이트 단위로 하드로 옮깁니다.
이 과정을 다루는 클래스는 System.IO.Stream에 정의되어 있습니다. 이 클래스는 추상 클래스이므로 직접 인스턴스를 만들어 사용할 수는 없고, 이를 상속받은 파생클래스로 작업이 이뤄집니다. FileStream, BufferedStream, MemoryStream 등이 그것입니다.
FileStream의 인스턴스를 만드는 법은 다음과 같습니다.
Stream fs1 = new FileStream("a.dat", FileMode.Create); //새파일 생성
Stream fs2 = new FileStream("b.dat", FileMode.Open); // 파일 열기
Stream fs3 = new FileStream("c.dat", FileMode.OpenOrCreate); //열거나 없으면 생성
Stream fs4 = new FileStream("d.dat", FileMode.Truncate); //파일 비워서 열기
Stream fs5 = new FileStream("e.dat", FileMode.Append); //덧붙이기 모드로 열기
인스턴스를 만들었으면 파일을 읽고 쓸 차례입니다.
쓰는데 이용되는 메소드로 Write()와 WriteByte()가 있습니다.
이 메소드에 쓰이는 매개변수는 byte또는 byte[]입니다. 파일을 읽고 쓰는 기본 단위가 byte라는 것입니다. 이래서는 C#에서 쓰이는 많은 데이터형을 처리하기가 어렵습니다.
따라서 BitConverter이라는 클래스를 이용하여 데이터를 byte형으로 바꾸거나 byte에 담긴 데이터를 다른 형식으로 변환해줄 수가 있습니다(BitConverter Class).
다음은 int형식의 데이터를 변환하여 파일에 쓰는 예입니다.
int value = -16;
Stream fs = new FileStream("a.dat", FileMode.Create); //파일 스트림 생성
Byte[] bytes = BitConverter.GetBytes(value); //값을 byte배열로 변환
fs.Write(bytes, 0, bytes.Length); // byte 배열을 파일에 기록
fs.Close(); // 파일스트림 닫기
읽기 역시 Read(), ReadByte() 메소드로 유사한 과정을 거칩니다.
Stream fs1 = new FileStream("a.dat", FileMode.Create); //새파일 생성
Stream fs2 = new FileStream("b.dat", FileMode.Open); // 파일 열기
Stream fs3 = new FileStream("c.dat", FileMode.OpenOrCreate); //열거나 없으면 생성
Stream fs4 = new FileStream("d.dat", FileMode.Truncate); //파일 비워서 열기
Stream fs5 = new FileStream("e.dat", FileMode.Append); //덧붙이기 모드로 열기
인스턴스를 만들었으면 파일을 읽고 쓸 차례입니다.
쓰는데 이용되는 메소드로 Write()와 WriteByte()가 있습니다.
이 메소드에 쓰이는 매개변수는 byte또는 byte[]입니다. 파일을 읽고 쓰는 기본 단위가 byte라는 것입니다. 이래서는 C#에서 쓰이는 많은 데이터형을 처리하기가 어렵습니다.
따라서 BitConverter이라는 클래스를 이용하여 데이터를 byte형으로 바꾸거나 byte에 담긴 데이터를 다른 형식으로 변환해줄 수가 있습니다(BitConverter Class).
다음은 int형식의 데이터를 변환하여 파일에 쓰는 예입니다.
int value = -16;
Stream fs = new FileStream("a.dat", FileMode.Create); //파일 스트림 생성
Byte[] bytes = BitConverter.GetBytes(value); //값을 byte배열로 변환
fs.Write(bytes, 0, bytes.Length); // byte 배열을 파일에 기록
fs.Close(); // 파일스트림 닫기
읽기 역시 Read(), ReadByte() 메소드로 유사한 과정을 거칩니다.
byte[] bytes = new byte[8];
Stream fs = new FileStream("a.dat", FileMode.Open); //파일 스트림 생성
fs.Read(bytes, 0, bytes.Length); // 데이터를 읽어 bytes에 저장
int value = BitConverter.ToInt32(bytes, 0); // bytes의 값을 int로 변환
fs.Close();
이런 식입니다.
예제를 하나 만들어 사용방법을 알아봅니다.
using System;
using System.IO;
namespace IOExample
{
class Program
{
static void Main(string[] args)
{
int value = 123454321;
Console.WriteLine("{0,-1}: {1}", "Original value",value);
string path = @"c:\temp\a.dat";
//같은 이름 파일이 존재하면 지운다.
if (File.Exists(path))
{
File.Delete(path);
}
// 지정 경로에 파일 생성
Stream writeStream = new FileStream(path, FileMode.Create);
byte[] wBytes = BitConverter.GetBytes(value); //바이트로 변환
Console.Write("{0,-14}: ", "Byte array");
foreach (byte b in wBytes) //바이트값 출력
Console.Write("{0:2} ", b);
Console.WriteLine();
writeStream.Write(wBytes, 0, wBytes.Length);
writeStream.Close();
// 읽기
Stream readStream = new FileStream(path, FileMode.Open);
byte[] rBytes = new byte[4];
int i = 0;
while (readStream.Position < readStream.Length)
rBytes[i++] = (byte)readStream.ReadByte();
int convertedValue = BitConverter.ToInt32(rBytes, 0);
Console.WriteLine("{0,-13} : {1}", "Data read", convertedValue);
readStream.Close();
}
}
}
| cs |
위의 예제에서 readStream에 Position이라는 속성이 사용되었습니다. 바이트 단위로 현재 읽어들이고 있는 위치를 뜻하는 속성입니다. Write()나 Read()를 사용할 때는 순차 접근 방식이 사용되므로 포지션을 옮겨가며 한 바이트씩 읽어들이게 됩니다.
Positon 속성이 있다는 얘기는 그 Positon을 변경하면 앞에서부터 순차적인 아닌 random Access가 가능하다는 뜻이겠죠. Seek()을 이용하면 파일 내의 임의의 위치로 옯겨가는 일이 가능해집니다.
readStream.Seek(5, Seekorigin.Current);
이렇게 하면 현재 위치로부터 5바이트 뒤로 이동하라는 뜻이 됩니다.
BinaryWriter, BinaryReader
FileStream은 모든 기능을 가지고 있지만 사용하기에 매우 불편합니다. byte형식의 데이터만 다룰 수 있다는 점이 그러합니다. 이를 돕기 위해 만들어진 것이 BinaryWriter와 BinaryReader입니다. BinaryWriter/Reader 클래스에 FileStream 인스턴스를 넘겨주면 2진 데이터의 형태로 BitConverter 없이 데이터 처리가 가능합니다. 만일 FileStream이 아닌 NetworkStream 인스턴스를 넘겨주면 네트워크로 보낼수도 있습니다.
BinaryWriter의 사용 방법을 살펴보겠습니다.
BinaryWriter writer = new BinaryWriter( new FileStream("a.dat", FileMode.Create));
writer.Write(123);
writer.Write("Wow!");
writer.Write(1.35); // 모든 형식을 오버로딩하고 있음
writer.Close();
BinaryReader도 대동소이합니다.
BinaryReader reader = new BinaryReader( new FileStream("a.dat", FileMode.Open));
int a = reader.ReadInt32();
string b = reader.ReadString();
double c = reader.ReadDouble(); // 형식별로 다른 이름의 메소드를 제공
reader.Close();
역시 예제를 만들어 보겠습니다.
결과)
File Size : 21 bytes
2147483647
Wow!
4294967295
1.79769313486232E+308
그러면 Stream클래스와 BinaryFormatter를 이용해 저장할 수 있는 형식이 됩니다.
[Serializable]
class MyClass
{
//
}
그 이후 다음과 같은 형식으로 사용 가능합니다.
Stream fs = new FileStream("a.dat", FileMode.Create);
BinaryFormatter serializer = new BinaryFormatter();
MyClass obj = new MyClass();
serializer.Serialize(fs, obj);
fs.Close();
BinaryFormatter는 System.Runtime.Serialization.Formatters.Binary 네임스페이스에 정의되어 있습니다.
읽어 들이는 과정도 유사합니다.
Stream fs1 = nee FileStream("a.dat", FileMode.Open);
BinaryFormatter deserializer = new BinaryFormatter();
MyClass obj = (MyClass)deserializer.Deserialize(fs1);
fs1.Close();
하면 됩니다.
만일 클래스 중간에 제외하고 싶은 필드가 있다면 그 필드 앞에 [NonSerialized]로 수식해주면 됩니다.
BinaryWriter의 사용 방법을 살펴보겠습니다.
BinaryWriter writer = new BinaryWriter( new FileStream("a.dat", FileMode.Create));
writer.Write(123);
writer.Write("Wow!");
writer.Write(1.35); // 모든 형식을 오버로딩하고 있음
writer.Close();
BinaryReader도 대동소이합니다.
BinaryReader reader = new BinaryReader( new FileStream("a.dat", FileMode.Open));
int a = reader.ReadInt32();
string b = reader.ReadString();
double c = reader.ReadDouble(); // 형식별로 다른 이름의 메소드를 제공
reader.Close();
역시 예제를 만들어 보겠습니다.
using System;
using System.IO;
namespace BinaryExample
{
class Program
{
static void Main(string[] args)
{
BinaryWriter writer = new BinaryWriter(new FileStream("a.dat", FileMode.Create));
writer.Write(int.MaxValue);
writer.Write("Wow!");
writer.Write(uint.MaxValue);
writer.Write(double.MaxValue);
writer.Close();
BinaryReader reader = new BinaryReader(new FileStream("a.dat", FileMode.Open));
Console.WriteLine("File Size : {0} bytes",reader.BaseStream.Length);
Console.WriteLine("{0}",reader.ReadInt32());
Console.WriteLine("{0}",reader.ReadString());
Console.WriteLine("{0}",reader.ReadUInt32());
Console.WriteLine("{0}",reader.ReadDouble());
reader.Close();
}
}
}
| cs |
결과)
File Size : 21 bytes
2147483647
Wow!
4294967295
1.79769313486232E+308
StreamWriter, StreamReader
텍스트 파일을 다루는 클래스입니다. Binary의 경우와 거의 동일하므로 바로 예제를 만들어 보겠습니다.
using System;
using System.IO;
namespace BinaryExample
{
class Program
{
static void Main(string[] args)
{
string path = @"c:\temp\a.txt";
if (File.Exists(path))
{
File.Delete(path);
}
StreamWriter writer = new StreamWriter(new FileStream(path, FileMode.Create));
writer.WriteLine(int.MaxValue);
writer.WriteLine("Wow!");
writer.WriteLine(uint.MaxValue);
writer.WriteLine(double.MaxValue);
writer.Close();
StreamReader reader = new StreamReader(new FileStream(path, FileMode.Open));
Console.WriteLine("File Size : {0} bytes",reader.BaseStream.Length);
while (reader.EndOfStream == false)
{
Console.WriteLine(reader.ReadLine());
}
reader.Close();
}
}
}
| cs |
Serialization
기본 형식을 읽고 쓰는 방법은 알았는데 클래스나 구조체같이 사용자가 정의한 데이터형은 어떻게 할까요?
C#에서는 직렬화(Serialization)이라는 방법으로 객체의 상태를 바이너리(뿐만 아니라 XML, JSON으로도 가능)로 바꿔줍니다.
그 방법은 아주 간단하여 저장하고자 하는 클래스 앞에 [Serializable]이라는 Attribute를 붙여주기만 하면 됩니다.
그러면 Stream클래스와 BinaryFormatter를 이용해 저장할 수 있는 형식이 됩니다.
[Serializable]
class MyClass
{
//
}
그 이후 다음과 같은 형식으로 사용 가능합니다.
Stream fs = new FileStream("a.dat", FileMode.Create);
BinaryFormatter serializer = new BinaryFormatter();
MyClass obj = new MyClass();
serializer.Serialize(fs, obj);
fs.Close();
BinaryFormatter는 System.Runtime.Serialization.Formatters.Binary 네임스페이스에 정의되어 있습니다.
읽어 들이는 과정도 유사합니다.
Stream fs1 = nee FileStream("a.dat", FileMode.Open);
BinaryFormatter deserializer = new BinaryFormatter();
MyClass obj = (MyClass)deserializer.Deserialize(fs1);
fs1.Close();
하면 됩니다.
만일 클래스 중간에 제외하고 싶은 필드가 있다면 그 필드 앞에 [NonSerialized]로 수식해주면 됩니다.
댓글 없음:
댓글 쓰기