[C++] 파일 입출력 (fstream)

·2023년 8월 24일
0

C++

목록 보기
15/17
post-custom-banner

fstream

// 파일에서의 입출력
#include <fstream>
#include <iostream>
#include <string>

int main() 
{
  // 파일 읽기 준비
  std::ifstream in("test.txt");
  std::string s;

  if (in.is_open()) 
  {
    in >> s;
    std::cout << "입력 받은 문자열 :: " << s << std::endl;
  } 
  else 
  {
    std::cout << "파일을 찾을 수 없습니다!" << std::endl;
  }
  return 0;
}

cout이나 cin은 표준 입출력에 연동 되어 있는 상황이었지만
파일 입출력의 경우 어느 파일에 입출력을 해야 할 지 지정 해야 함
-> 지정 방법은 ifstream 객체의 생성자에 연동하고자 하는 파일의 경로를 전달하면 됨

파일 경로를 지정하면 해당하는 파일을 찾고 열게 됨
만약 파일이 존재하지 않는다면 파일을 열 수 없음 -> 파일이 열렸는지 유무는 아래 함수로 확인 가능

if (in.is_open()) // 해당 경로에 파일이 존재하지 않으면 false를 리턴함

cin 처럼 in 객체를 이용해서 파일로부터 읽을 수 있음

in >> s;

ifstream 객체의 소멸자에서 자동으로 close를 해 주기 때문에 C언어 때 처럼 fclose()를 해줄 필요 없음
그러나 예외적으로 close를 직접 해야 하는 경우가 있음

#include <fstream>
#include <iostream>
#include <string>

int main() 
{
  // 파일 읽기 준비
  std::ifstream in("test.txt");
  std::string s;

  if (in.is_open()) 
  {
    in >> s;
    std::cout << "입력 받은 문자열 :: " << s << std::endl;
  } 
  else 
  {
    std::cout << "파일을 찾을 수 없습니다!" << std::endl;
  }

  in.close();
  in.open("other.txt");

  if (in.is_open()) 
  {
    in >> s;
    std::cout << "입력 받은 문자열 :: " << s << std::endl;
  } 
  else 
  {
    std::cout << "파일을 찾을 수 없습니다!" << std::endl;
  }

  return 0;
}

위와 같이 새로운 파일에서 같은 객체가 입력을 받기 위해서는 기존 파일 스트림과의 연결을 종료하고
새로운 파일을 연결시켜 주면 됨
open 함수를 이용하면 새로운 파일과 연결이 가능
(이는 ifstream 객체 생성자에서 파일 경로를 바로 지정하지 않고 open으로 원하는 때에 필요한 파일을 열어도 됨을 의미)


파일 전체를 한 번에 읽기

#include <fstream>
#include <iostream>
#include <string>

int main() 
{
  // 파일 읽기 준비
  std::ifstream in("test.txt");
  std::string s;

  if (in.is_open()) 
  {
    // 위치 지정자를 파일 끝으로 옮긴다.
    in.seekg(0, std::ios::end);

    // 그리고 그 위치를 읽는다. (파일의 크기)
    // tellg 함수는 위치 지정자의 위치 (시작 지점으로 부터의)를 반환함
    int size = in.tellg();

    // 그 크기의 문자열을 할당한다.
    s.resize(size);

    // 위치 지정자를 다시 파일 맨 앞으로 옮긴다.
    in.seekg(0, std::ios::beg);

    // 파일 전체 내용을 읽어서 문자열에 저장한다.
    in.read(&s[0], size);
    std::cout << s << std::endl;
  } 
  else 
  {
    std::cout << "파일을 찾을 수 없습니다!" << std::endl;
  }

  return 0;
}

파일 전체를 한 줄씩 읽기

// getline 으로 읽어들이기
#include <fstream>
#include <iostream>
#include <string>

int main() 
{
  // 파일 읽기 준비
  std::ifstream in("test.txt");
  char buf[100];

  if (!in.is_open()) 
  {
    std::cout << "파일을 찾을 수 없습니다!" << std::endl;
    return 0;
  }

  while (in) 
  {
    in.getline(buf, 100);
    std::cout << buf << std::endl;
  }

  return 0;
}

개행 문자(\n)가 나올 때 까지 최대 지정한 크기 - 1만큼 읽게 됨
(하나 적게 읽는 이유는 buf의 맨 마지막 문자로 널 종료 문자를 넣어줘야 하기 때문)

지정한 문자가 나올 때 까지 읽기

in.getline(buf, 100, '.');

while문 조건에 in을 전달하면 bool로 캐스팅 하는 연산자가 호출됨
이 때 in이 true이기 위해서는 다음 입력 작업이 성공적이여야 하고, 현재 스트림에 오류 플래그가 켜져 있지 않아야 함
하지만 getline 함순는 개행 문자가 나오기 전에 지정한 버퍼의 크기가 다 차게 되면 failbit를 키므로
버퍼의 크기를 너무 작게 만들면 정상적으로 데이터를 받을 수 없음
-> 이런 한계점을 극복하기 위해 std::string에서 getline 함수를 제공함

// std::string 에 정의된 getline 사용
#include <fstream>
#include <iostream>
#include <string>

int main() 
{
  // 파일 읽기 준비
  std::ifstream in("test.txt");

  if (!in.is_open()) 
  {
    std::cout << "파일을 찾을 수 없습니다!" << std::endl;
    return 0;
  }

  std::string s;
  while (in) 
  {
    getline(in, s);
    std::cout << s << std::endl;
  }

  return 0;
}

이 getline 함수는 std::string에 정의되어 있는 함수로 첫 번째 인자로 istream 객체를 받고,
두 번째 인자로 입력 받은 문자열을 저장할 string 객체를 받게 됨
굳이 버퍼의 크기를 지정하지 않아도 알아서 개행문자 혹은 파일의 끝이 나올 때까지 입력을 받기 때문에
ifstream의 getline을 활용할 때 보다 편리함


주의사항

while문 조건으로 절대 in.eof()를 사용하면 안 됨
eof 함수는 파일 위치 지시자가 파일에 끝에 도달한 이후에 true를 리턴하기 때문임
-> 즉, in.eof()는 while문 안에서 파일 읽기가 안전하다라는 것을 보장하지 않음


파일에 쓰기

#include <iostream>
#include <fstream>
#include <string>

int main() 
{
  // 파일 쓰기 준비
  std::ofstream out("test.txt");

  std::string s;
  if (out.is_open()) 
  {
    out << "이걸 쓰자~~";
  }

  return 0;
}

test.txt가 존재하지 않을 경우 test.txt를 생성한 뒤에, 생성이 성공하면 출력함
ofstream은 열려는 파일이 존재하지 않으면 해당 파일을 생성하고 염
만약, 해당 파일이 존재하면 특별한 설정을 하지 않는다면 해당 파일 내용이 다 지워지고 새로운 내용으로 덮어 씌워지게 됨


파일에 이어쓰기

#include <fstream>
#include <iostream>
#include <string>

int main() 
{
  // 파일 쓰기준비
  std::ofstream out("test.txt", std::ios::app);

  std::string s;
  if (out.is_open()) 
  {
    out << "덧붙이기";
  }

  return 0;
}

out 객체를 생성할 때 옵션으로 std::ios::app을 주게 되면 파일에 스트림을 연결할 때 기존 파일의
내용을 지우고 새로 쓰는 것이 아니라 그 뒤에 새로운 내용을 붙여 쓰게 됨

  • ios::ate : 자동으로 파일 끝에서 부터 읽기와 쓰기 실시 (즉, 파일을 열 때 위치 지정자가 파일 끝을 가리킴)
  • ios::trunc : 파일 스트림을 열면 기존에 있던 내용들이 모두 지워짐
  • ios::in, std::ios::out : 파일에 입력을 할 지 출력을 할 지 지정

ios::ate와 ios::app의 차이

ios::app이 경우 원본 파일의 내용을 무조건적으로 보장함
-> 파일 위치 지정자가 기존 파일의 끝이 시작점이라 생각하며 움직임
ate는 위치 지정자를 그 이전으로 옮길 수 있음
-> 기존 파일을 포함해서 움직임
(그렇다고 해서 ate를 이용해서 기존에 있는 파일 데이터 한 가운데에 무언가를 끼워 넣을 수 있는 것은 아님)

// ate 와 app
#include <fstream>
#include <iostream>
#include <string>

int main() 
{
  // 두 파일에는 모두 abc 라고 써 있음
  std::ofstream out("test.txt", std::ios::app);
  std::ofstream out2("test2.txt", std::ios::ate);

  out.seekp(3, std::ios::beg);
  out2.seekp(3, std::ios::beg);

  out << "추가";
  out2 << "추가";

  return 0;
}

app을 사용한 경우에는 'abc추가'로 파일 내에 표기되지만
ate는 ' 추가'로 표기 됨
즉, 위치 지정자는 움직여서 3칸 뒤에 출력되지만 기존에 써져 있던 내용은 모두 지워짐


std::ofstream 연산자 오버로딩 하기

#include <fstream>
#include <iostream>
#include <string>

class Human {
  std::string name;
  int age;

 public:
  Human(const std::string& name, int age) : name(name), age(age) {}
  std::string get_info() {
    return "Name :: " + name + " / Age :: " + std::to_string(age);
  }

  friend std::ofstream& operator<<(std::ofstream& o, Human& h);
};

std::ofstream& operator<<(std::ofstream& o, Human& h) {
  o << h.get_info();
  return o;
}
int main() {
  // 파일 쓰기 준비
  std::ofstream out("test.txt");

  Human h("이재범", 60);
  out << h << std::endl;

  return 0;
}

문자열 스트림 (std::stringstream)

#include <iostream>
#include <sstream>

int main() 
{
  std::istringstream ss("123");
  int x;
  ss >> x;

  std::cout << "입력 받은 데이터 :: " << x << std::endl;

  return 0;
}

sstream의 std::istringstream은 마치 문자열을 하나의 스트림이라 생각하게 해주는 가상화 장치

std::istringstream ss("123");

파일에 123이라 기록해놓고 거기서 입력 받는 것과 동일
-> 즉, atoi 함수를 사용할 필요 없이 간편하게 문자열에서 숫자로 변환하는 함수를 만들 수 있음

ostringstream을 이용해서 숫자를 문자열로 바꾸는 것도 가능

post-custom-banner

0개의 댓글