C# StreamReader ,StreamWriter, 파일 읽기, 쓰기

2024. 4. 26. 19:45C#/C# 학습정리

파일 읽기, 쓰기를 할 수있도록 도와주는 C# StreamReader, StreamWriter에 대해서 공부해보았다. 알게 된 계기가 TextRPG프로젝트를 진행하면서 프로그램이 종료가 되어도 데이터를 저장하거나 불러올 수 있는 로직을 구현해야 했다. 몇가지의 인스턴스들의 데이터들을 간단하게 텍스트 파일로 저장하고 불러오기 위해서 공부했다.

 

목차

    1. StreamReader
    2. StreamWriter
    3. 실제 게임에 적용한 부분

    StreamReader와 StreamWriter

    C#에서 파일을 읽고 쓰기 위한 스트림이다. System.io를 사용해야한다.

    클래스로 구현되어있고 다양한 메서드들을 C#에서 제공하여 파일을 읽고 쓸 수 있다.

     


    StreamReader

    파일 읽기

     

    생성할때도 클래스의 객체를 생성하듯이 생성해야한다. 생성자는 매우 다양하게 있으며 보통 "path"를 넣는다.

    var reader = new StreamReader(path);

    생성할때 들어가는 path는 확장자+파일명 전체를 넣거나 파일의 경로를 넣어 전달하면 된다. 읽기를 시도할 파일이 실행한 exe프로그램과 같은 경로에있을 경우 파일명만 적어도 올바르게 동작한다.

     

    예외

    • ArgumentException
      • stream이 읽기를 지원하지 않습니다.
    • ArgumentNullException
      • stream이(가) null인 경우

    메서드

    여러 메서드가 있는데 간단한

    Read()

    ReadLine()을 살펴봤다.

     

    data.txt를 프로그램과 같은 경로에 생성하였고 데이터를 넣었다.
    Read()

    static void Main(string[] args)
    {
        StreamReader reader = new StreamReader("data.txt");
        Console.WriteLine((char)reader.Read());
    }

    실행하면 Read()를했을때 AscII코드로 읽어오기때문에 반환형이 int이다. 따라서 (char)로 명시적 형변환을 해주어 내가 원하는 값을 얻을 수 있다.

    맨 앞글자 1만 따와진 모습이다.

     

    ReadLine()

    static void Main(string[] args)
    {
        StreamReader reader = new StreamReader("data.txt");
        Console.WriteLine(reader.ReadLine());
    }

    ReadLine()메서드는 반환형이 String이기때문에 따로 변환을 안해줘도 텍스트에 적혀있는 글자가 그대로 나온다. ReadLine()은 한 줄을 다 읽기(개행문자를 만나기 전까지)때문에 데이터를 줄간격으로 읽을때 편리하다. 만약 스트림의 끝을 읽은 경우는 null을 반환한다.

    static void Main(string[] args)
    {
        StreamReader reader = new StreamReader("data.txt");
    
        string line;
        while((line = reader.ReadLine()) != null)
        {
            Console.WriteLine(reader.ReadLine());
        }
        reader.Close();
    }

    파일의 데이터를 다 읽고 싶다면 위와 같이 while문을 작성해 null을 리턴할때까지 계속해서 파일을 읽을 수 있다.

    data.txt의 모든 줄을 읽어옴


     

    StreamWriter

    파일쓰기

    파일에 쓰기는 읽기랑 생성자의 매개변수 방식이 똑같다. StreaWriter객체를 생성하고 사용하면 된다.

    var writer = new StreamWriter("path");

    마찬가지로 path는 경로를 넣어주면 된다.

     

    예외

    • ArgumentException
      • stream에 쓸 수 없습니다.
    • ArgumentNullException
      • Stream이 null인 경우

    메서드

    간단하게 Write()와 WriteLine()만 살펴보았다.

    static void Main(string[] args)
    {
        StreamWriter writer = new StreamWriter("data.txt");
    
        writer.Write("Hello");
        writer.Write(", World!");
    }

    StreamWriter.Write(string str) 메서드는 str을 그대로 스트림에 써주는 것이다. 

    static void Main(string[] args)
    {
        StreamWriter writer = new StreamWriter("data.txt");
    
        string str = "Hello";
        string str2 = ", World!";
        writer.Write(str);
        writer.Write(str2);
        writer.Close();
    }

     

    여기서 주의해야 할 점이 세 가지가 있다.

    • writer를 닫아주지 않으면 파일에 쓰려는 데이터가 버퍼에만 쌓여있고 실제로 파일에 write되지 않는다.
    • Write를 하면 개행(줄바꿈)을 해주지 않기 때문에 바로 옆에 쓰인다.
    • 파일을 아예 새로 쓰는것이기 때문에 기존의 내용이 사라진다.

    실제로 옆에 쓰였다

    첫번째 문제는 writer.Close()메서드를 이용해 writer를 닫아줘야한다.

    두번째 문제는 WriteLine(string str)을 사용하면 str을 파일에 write하고 자동으로 개행시켜준다.

    세번째 문제는 파일에 이어서 쓰려면 Write클래스 생성자에 Append 옵션이 있다. 해당 옵션을 사용하면 파일을 덮어쓰지 않고 파일의 뒤에 추가한다.

     

    StreamWriter(string path, bool append)

    append가 default옵션으로 false처리가 되어있고, true옵션을 넘기면 파일 뒤부터 작성한다


     

    실제 게임에 적용한 부분

     public class Character
     {
         private static Character instance;
         public int lv { get; set; }
         public string chad { get; set; }
         public string name { get; set; }
    
         public float attack_ab { get; set; }
         public int add_attack_ab;
    
         public int defense_ab { get; set; }
         public int add_defense_ab;
         public int health_ab { get; set; }
         public int add_health_ab;
    
         public bool isDead { get; set; }
    
         public int moneyAmount { get; set; }
    }

    해당 데이터를 가진 캐릭터 클래스의 인스턴스를 저장하고 불러오기를 위해 StreamWriter, StreamReader를 사용하였다.

     

    저장하기

    string filePath = "characterData.txt";
    public void SaveToFile(string filePath)
     {
         using (StreamWriter writer = new StreamWriter(filePath))
         {
             writer.WriteLine(lv);
             writer.WriteLine(chad);
             writer.WriteLine(name);
             writer.WriteLine(attack_ab);
             writer.WriteLine(defense_ab);
             writer.WriteLine(health_ab);
             writer.WriteLine(moneyAmount);
             writer.WriteLine(isDead);
         }
         //Console.WriteLine("Character 데이터가 저장되었습니다.");
         //Console.WriteLine();
     }

     

    using문을 사용하였는데 해당 using문 내에서 writer객체를 생성하고 사용하면 using문이 끝날때 자동으로 writer 리소스를 해제할 수 있기 때문이다. 자동으로 파일을 닫고 파일에 쓴 데이터를 사용하고 자원을 해제할 수 있다.

    해당 코드에서 class가 가진 필드들을 모두 WriteLine을 해주어 텍스트 파일에 저장한다. 그러면 다음과 같이 저장 된다.

     

     

    라인별로 저장을하고 나중에 불러올때도 라인별로 텍스트파일을 읽어서 새로운 인스턴스를 만들어 플레이어에게 제공하는 방식으로 구현하였다.

     

     public void LoadToCharacter(string filePath)
     {
         using (StreamReader reader = new StreamReader(filePath))
         {
             lv = int.Parse(reader.ReadLine());
             chad = reader.ReadLine();
             name = reader.ReadLine();
             attack_ab = float.Parse(reader.ReadLine());
             defense_ab = int.Parse(reader.ReadLine());
             health_ab = int.Parse(reader.ReadLine());
             moneyAmount = int.Parse(reader.ReadLine());
             isDead = bool.Parse(reader.ReadLine());
         }
         //Console.WriteLine("Character 데이터를 불러왔습니다.");
         //Console.WriteLine();
     }

    해당 LoadToCharacter()메서드도 마찬가지로 filePath의 라인별로 읽어 새롭게 인스턴스에 할당하는 방식으로 구현되어있다. 이렇게해서 간단한 콘솔 TextRPG에서 게임을 저장하고 불러올 수 있는 기능을 구현해보았다.


     

     

     

     

    'C# > C# 학습정리' 카테고리의 다른 글

    C# - Queue  (0) 2024.05.02
    C# - Event  (1) 2024.04.30
    C# - Stack  (0) 2024.04.29
    C# 상속과 다형성  (0) 2024.04.24
    C# Ref 와 Out 키워드  (0) 2024.04.23