[C#] 41. 파일 다루기(IO)와 파일 메타 데이터(FileInfo) 사용하기


Study/C#  2021. 10. 1. 18:10

안녕하세요. 명월입니다.


이 글은 C#에서 파일 다루기(IO)와 파일 메타 데이터(FileInfo) 사용하기에 대한 글입니다.


프로그램에서 사용되는 대표적인 장치 리소스라고 하면 파일 다루기(파일로 저장과 읽기, IO)와 통신 소켓(Socket)이 있습니다.

이 파일 다루기는 영어의 약어로 IO라고 표현합니다만, IO는 Input ouput으로 입출력이라는 뜻입니다. 입출력 장치로는 마우스, 키보드, 모니터 등등이 있습니다만 프로그램에서 IO라고 하면 파일을 다루는 리소스라고 생각하면 됩니다.


우리가 프로그램을 종료하면 메모리 상에 등록되어 있던 데이터가 모두 사라지게 됩니다. 그래서 프로그램 상에서 생성된 데이터를 보관하는 방법의 한가지로 파일로 보관하는 방법이 있습니다.

파일은 byte 형식의 형식으로 구성되어 있고, 프로그램에서는 unsigned char(byte)로 구성을 합니다.

using System;
using System.IO;
using System.Text;

namespace Example
{
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // FileStream 생성
      // 생성할 파일 이름, 옵션 (파일 생성), 파일 접근 권한(작성)
      var stream = new FileStream("d:\\work\\test.txt", FileMode.Create, FileAccess.Write);
      try
      {
        // 파일에 입력할 string
        var str = "Hello world";
        // String을 byte[] 형식으로 작성
        var binary = Encoding.UTF8.GetBytes(str);
        // 파일 쓰기
        stream.Write(binary, 0, binary.Length);
      }
      finally
      {
        // 스트림 닫기
        stream.Close();
      }

      // 아무 키나 누르면 종료
      Console.WriteLine("Press Any key...");
      Console.ReadLine();
    }
  }
}

저는 d 드라이브에 work라는 폴더에 test.txt라는 파일 작성했습니다.

내용은 Hello world로 정확히 띄어쓰기까지 11바이트를 생성합니다. 결과를 보면 파일 사이즈가 11바이트로 생성된 것을 확인할 수 있습니다.


파일을 txt파일로 작성하였고 String 데이터를 Encoding을 통해 byte 형식으로 작성하였습니다.

그렇기 때문에 메모장에서 해당 파일을 열면 Hello world의 내용이 작성된 것을 확인할 수 있습니다.


그리고 최종적으로 리소스를 사용후에는 Close 함수로 닫아야 문제가 없습니다.


이번에는 파일의 데이터를 읽어서 콘솔에 표시하겠습니다.

using System;
using System.IO;
using System.Text;

namespace Example
{
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // 파일 메타 데이터 취득
      var file = new FileInfo("d:\\work\\test.txt");
      // FileStream 생성
      // 생성할 파일 이름, 옵션 (파일 열기), 파일 접근 권한(읽기)
      var stream = new FileStream("d:\\work\\test.txt", FileMode.Open, FileAccess.Read);
      try
      {
        // 파일 메타 데이터로 파일 크기를 취득해 와서 byte 배열 생성
        var binary = new byte[file.Length];
        // stream으로 파일 읽기
        stream.Read(binary, 0, binary.Length);
        // byte 형식을 string 형식으로 변한
        var str = Encoding.UTF8.GetString(binary);
        // 콘솔 출력
        Console.WriteLine(str);
      }
      finally
      {
        // 스트림 닫기
        stream.Close();
      }

      // 아무 키나 누르면 종료
      Console.WriteLine("Press Any key...");
      Console.ReadLine();
    }
  }
}

위는 파일을 읽어와서 콘솔에 표시하는 예제입니다.

파일 메타 데이터로 파일의 정보를 읽어와서 파일 크기만큼 byte 배열을 생성합니다.

그리고 stream으로 읽어서 String으로 변환했습니다. 변환한 내용을 다시 콘솔에 표시하니 Hello world가 표시됩니다.


여기서 먼저 Encoding과 바이너리(byte[])에 대해서는 다음 글에서 자세히 설명하겠습니다.


먼저 FileStream에 대해서는 첫번째 파라미터는 대상 파일 경로, 파일 모드 설정, 파일 접근 권한 설정으로 스트림을 생성합니다.

파일 모드로는 일반적으로 Create, Open, Write, CreateNew, Append를 사용하는데 Create는 파일을 생성할 때(기존 파일이 있으면 덮어쓰기), CreateNew도 파일 생성입니다만 기존 파일이 있으면 에러를 발생합니다.

Open은 기존 파일을 읽어올때, Write와 Append는 작성하는 것입니다만, Write는 기존 내용을 삭제하고 처음부터 작성하는 것, Append는 기존 내용에 이어 붙여서 작성하는 것입니다.

파일 접근 권한은 Read는 읽기, Write는 쓰기, ReadWrite는 읽고 쓰기의 동시 기능을 사용합니다.

그리고 Stream은 사용하고 나서 꼭 Close를 하여야 하는데, 왜냐하면 Stream이 Open 되어 있는 상황에서는 파일이 리소스를 점유하고 있기 때문입니다.

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

namespace Example
{
  class Program
  {
    // 쓰레드로 파일 읽기 함수
    private static async Task ReadFile()
    {
      // Task 생성
      var task = new Task(() =>
      {
        // FileStream 생성
        // 생성할 파일 이름, 옵션 (파일 열기), 파일 접근 권한(읽기)
        var stream = new FileStream("d:\\work\\test.txt", FileMode.Create, FileAccess.Write);
        try
        {
          // 파일에 입력할 string
          var str = "Hello world";
          // String을 byte[] 형식으로 작성
          var binary = Encoding.UTF8.GetBytes(str);
          // 파일 쓰기
          stream.Write(binary, 0, binary.Length);
        }
        finally
        {
          // 스트림 닫기
          //stream.Close();
        }
      });

      task.Start();
      await task;
    }
    // 실행 함수
    static void Main(string[] args)
    {
      // 쓰레드 시작
      ReadFile().Wait();
      ReadFile().Wait();

      // 아무 키나 누르면 종료
      Console.WriteLine("Press Any key...");
      Console.ReadLine();
    }
  }
}

쓰레드로 병렬 처리를 만든 다음에 각각의 쓰레드에서 FileStream을 생성해서 파일 쓰기를 만들었습니다. 그리고 마지막에 stream.Close()를 주석 처리하고 실행을 했습니다.

그렇게 하니 실행 중에 에러가 발생하네요. 에러 내용은 일본어로 쓰여 있지만, 다른 프로세스에서 해당 리소스가 사용 중이라서 접근할 수 없다는 에러가 발생하네요.

우리가 간혹 엑셀이나 파일을 동시에 두세개 열게 되면, 나는 에러와 비슷한 개념입니다. Stream으로 파일 리소스를 점유하게 되면 다른 프로그램 또는 다른 실행 영역에서 사용할 수 없다는 에러가 발생합니다.


그리고 파일의 데이터는 사실 바이너리 데이터 밖에 없습니다. 위 예제에서 Hello world의 11byte의 데이터를 넣으니 데이터 사이즈는 정확하게 11byte가 되는 것을 확인할 수 있습니다.

그런데 우릭 파일 정보를 보면 파일 이름부터 작성 일시, 변경 일시, 접근 일시등의 데이터가 있는데 이는 파일에 이 정보를 가지고 있는게 아니고 운영체제(OS)에서 파일의 정보를 가지고 있는 것으로 메타 데이터라고 합니다.

이 메타 데이터는 FileInfo 클래스를 이용해서 정보를 취득, 변경을 할 수 있습니다.

using System;
using System.IO;

namespace Example
{
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // 파일 메타 데이터 취득
      var fileinfo = new FileInfo("d:\\work\\test.txt");
      // 파일 이름
      Console.WriteLine("File Name - " + fileinfo.FullName);
      // 파일 크기
      Console.WriteLine("File size - " + fileinfo.Length);
      // 파일 작성 일시
      Console.WriteLine("File CreationTime - " + fileinfo.CreationTime);
      // 파일 마지막으로 작성된 일시
      Console.WriteLine("File LastWriteTime - " + fileinfo.LastWriteTime);
      // 파일 마지막으로 접근한 일시
      Console.WriteLine("File LastAccessTime - " + fileinfo.LastAccessTime);

      // 아무 키나 누르면 종료
      Console.WriteLine("Press Any key...");
      Console.ReadLine();
    }
  }
}

그리고 이 메타 데이터는 프로그램 내에서는 수정도 가능합니다.

using System;
using System.IO;

namespace Example
{
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // 파일 메타 데이터 취득
      var fileinfo = new FileInfo("d:\\work\\test.txt");
      // 파일 작성 일시 수정
      fileinfo.CreationTime = new DateTime(1990, 01, 01);
      // 파일 마지막으로 작성된 일시 수정
      fileinfo.LastWriteTime = new DateTime(1990, 01, 02);
      // 파일 마지막으로 접근한 일시
      fileinfo.LastAccessTime = new DateTime(1990, 01, 03);

      // 아무 키나 누르면 종료
      Console.WriteLine("Press Any key...");
      Console.ReadLine();
    }
  }
}

메타 데이터를 수정하여 파일 정보를 확인하니 전부 1990년에 작성되고 접근 된 것으로 확인할 수 있습니다.


여기까지 C#에서 파일 다루기(IO)와 파일 메타 데이터(FileInfo) 사용하기에 대한 글이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.