// 파일에서의 입출력
#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::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칸 뒤에 출력되지만 기존에 써져 있던 내용은 모두 지워짐
#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;
}
#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을 이용해서 숫자를 문자열로 바꾸는 것도 가능