C++ - file

mohadang·2022년 9월 17일
0

C++

목록 보기
5/48
post-thumbnail

ifstream

  • 파일 입력
  • get, getline, operator>>() 함수 존재
  • EX
ifstream fs;
fs.open("test.txt", ios_base::trunc);
char buf[100];
while (true)
{
  fs.getline(buf, sizeof(buf));
  cout << buf << endl;

  if (fs.eof())
  {
    cout << "[End]" << endl;
    break;
  }

  if (fs.fail())
  {
    cout << "[Fail]" << endl;
    fs.clear();
    fs.ignore(LLONG_MAX, '\n');
    continue;
  }
}
fs.close();

/*
100 200 300 을 읽는다고 가정 할 때 파일 포인터는

V
100 200 300 EOF

    V
100 200 300 EOF

        V
100 200 300 EOF

            V
100 200 300 EOF

순으로 가리킨다.
*/
  • get
    • \n은 스트림에 남긴채 문자열을 읽어옴
    • 한 문자 읽기 가능
  • getline
    • \n을 스트림에서 버리고 문자열을 읽어옴, \n을 읽어와서 buffer에 저장하는게 아니라 그냥 버림...
  • >> 연산자
    • >> 연산자는 ' '이나 '\n'등을 구분자로 두어 문자열을 읽어 들인다.
int number;
fin >> number;

/*
100 '\n' 300
100 읽고 \n 읽어 들이지만 숫자 아니라 값은 못 읽는다.
그 후에 파일 포인터는 300을 가리킨다.
*/

ofstream

  • 파일 출력
  • write, operator<<(), ignore 함수 존재
  • EX
ofstream fs;
fs.open("test.txt");

char buf[100] = "ABCDEF";

fs << buf << " " << "This is My Life";

fs.close();

/*
기존에 파일이 존재할 경우 fs.open("test.txt"); 로 다시 열면 파일에 있는 내용 모두 사라진다.
fstream으로 열면 사라지지 않는다. 그러나 ios_base::out 옵션 주면 사라진다.
*/
  • EX : ios_base::in 속성을 줄 때
//[test.txt]
//
//aaaaaaaaaaaaaaaaaaa
ofstream fs;
fs.open("test.txt", ios_base::in);

char buf[100] = "123";
fs << buf;
fs.close();

/*
결과 :
  123aaaaaaaaaaaaaaaa

ios_base::in 속성을 주니까 파일을 삭제하지 않는다.
ios_base::in | ios_base::trunc 속성을 주면 삭제 한다.
*/

fstream

  • 파일 입력 및 출력
  • ifstream과 ofstream의 혼용 버전, 그래서 각 함수들 모두 가지고 있음.
  • 당연히 fstream은 내부에 input, output stream이 따로 제공할까??
    • 객체가 하나인 만큼 하나의 스트림이다.
  • fstream은 파일을 자동으로 생성하지 않는다.
fstream fs;
fs.open("test.txt");
if (fs.is_open() == false)
{
  cout << "not open" << endl;
  return 0;
}

/*
파일 없을 경우 생성하지 않는다.
단 ios_base::out 속성을 주면 생성한다.
*/
  • EX : Write
//test.txt
//
//aaaaaaaaaaaaaaaaaaa
fstream fs;
fs.open("test.txt");

char buf[100] = "123";
fs << buf;
fs.close();

/*
결과 :
  123aaaaaaaaaaaaaaaa
*/
  • EX : Read
fstream fs;
fs.open("test.txt");
char buf[100];
while (true)
{
  fs >> buf;

  cout << buf << endl;

  if (fs.eof())
  {
    cout << "[End]" << endl;
    break;
  }

  if (fs.fail())
  {
    cout << "-Fail-" << endl;
    fs.clear();
    fs.ignore(LLONG_MAX, '\n');
    continue;
  }
}
fs.close();
  • 스트림이 두개 있는듯 하다, 그리고 두 스트림은 읽거나 쓸데마다 똑같이 이동 된다
char buf[10];
while (true)
{
  fs.get(buf, 10);

  cout << buf << " : read => " << fs.tellg() << ", write => " << fs.tellp() << endl;

  if (fs.eof())
  {
    cout << "[End]" << endl;
    break;
  }

  if (fs.fail())
  {
    cout << "- Fail -" << endl;
    fs.clear();
    fs.ignore(LLONG_MAX, '\n');
    continue;
  }
}
fs.close();
/*
결과 :
  123aaaaaa : read => 9, write => 9
  aaaaaaaaa : read => 18, write => 18
  aaaaaa : read => 24, write => 24
  : read => -1, write => -1
  - Fail -
  : read => -1, write => -1
  [End]
*/

ifstream에 ios_base::out 을 주어서 읽는 행위를 해보려고 했지만 그럴 수 없었다. 이유는 ifstream에 write를 할 수 있는 함수가 없기 때문이다.

파일 스트림에 <<, >>, 조정자(manipulatior)등도 쓸 수 있음.

c 에서 File IO의 문제점

FILE *fp;

//읽기 전용으로 파일 오픈
fp = fopen("HelloWorld.txt". "r");

//쓰기 전용으로 파일 오픈(파일 없으면 생성)
fp = fopen("HelloWorld.txt". "w+");

//읽기와 쓰기 범용으로 파일 오픈
fp = fopen("HelloWorld.txt". "r+");


//우선 +가 무슨 뜻인지 직관적이지 않다 또한 rw 이렇게 조합까지 되면 더더욱 이해하기 어렵다
//"r"에다 이상한 문자열을 넣어도 컴파일 에러가 발생하지 않는다.

C++에서는 좀더 직관적이어졌다.

//(객체 이용)

ifstream fin;
fin.open("HelloWorld.txt");

ofstream fout;
fout.open("HelloWorld.txt");

fstream fs;
fs.open("HelloWorld.txt");
  • open()

    • 각 스트림마다 open()메서드가 있음

    • fin.open("hellowWorld.txt", ios_base::in | ios_base::binary);

    • mode flags

      • 네임스페이스

        • ios_base;
      • 모든 조합이 유효하지 않음

      • in : 입력

      • out : 출력

        • 파일 없으면 생성해서 만들어줌
      • ate : at the end, 마지막으로 파일 포인터 이동

      • app : 덧 붙이기

      • trunc : 파일 읽은 다음에 스트림 플러쉬, 파일이 있으면 해당 파일의 모든 데이터를 지우고 개방함.

      • binary : 이진

      • ifstream, ofstream, fstream 모두 플래그를 지정하지 않으면 설정되는 디폴트 플래그 있음

  • is_open()

    • stream이 열려 있는지 확인 할 수 있음.
  • close()

    • stream을 닫음.
  • C/C++ 플래그 비교
    "r" : ios_base::in
    "w" : ios_base::out
    "w" : ios_base::out | ios_base::trunc
    "a" : ios_base::out | ios_base::app
    "r+" : ios_base::in | ios_base::out
    "w+" : ios_base::in | ios_base::out | ios_base::trunc

  • C++에서 자주 사용되는 파일 모드 조합

    • ios_base::out | ios_base trunc / "w"
      • 파일을 쓰는 것만이 가능한 모드로 개방.
      • 파일이 없으면 새 파일을 만듬.
      • 파일이 있으면 해당 파일의 모든 데이터를 지우고 개방함.
    • ios_base::out | ios_base::app / "a"
      • 파일을 쓰는 것만이 가능한 모드로 개방함.
      • 파일이 없으면 새 파일을 만듬.
      • 파일이 있으면 해당 파일의 맨 끝에서부터 데이터를 추가함.
    • ios_base::in | ios_base::out / "r+"
      • 파일을 읽고 쓰는 것이 둘 다 가능한 모드로 개방함.
    • ios_base::in | ios_base::out | ios_base::trunc/ "w+"
      • 파일을 읽고 쓰는 것이 둘 다 가능한 모드로 개방함.
      • 파일이 없으면 새 파일을 만듬.
      • 파일이 있으면 해당 파일의 모든 데이터를 지우고 개방

파일 닫기

  • C
// 반드시 호출해주어야함
FILE *fp
fclose(fp); 
  • C++
// 반드시 호출해줄 필요 없음, fin이 메모리 소멸되면서 자동으로 파일 닫아줌
ifstream fin;
fin.close(); 

파일 스트림 상태

  • C
FILE* fp;
fp = fopen("HelloWorld.txt", "r+");
if(fp != NULL)
  • C++
fstream fs;
fs.open("HelloWorld.txt");
if(fs.is_open())

각 스트림마다 close(), is_open() 함수가 있음

파일에서 '한 문자' 읽기

  • C
FILE* fp;
fp = fopen("HelloWorld.txt", "r");
char ch;
do
{
  ch = getc(fp);
  printf("%c", ch);
}while(ch != EOF);

fclose(fp);
  • C++
ifstream fin;
fin.open("HellowWorld.txt");

char ch;
while(true)
{
  fin.get(ch);
  if(fin.fail())
  {
    break;
  }
  cout << ch;
}
fin.close();

get(), getline()

  • get 함수는 한 문자만 입력 받는 함수가 아니다. 문자열을 읽을 수 있으며 getline과 차이점은 get은 \n을 파일 스트림에 그대로 둔다.

  • 어떤 스트림(cin, istringstream)을 넣어도 동일하게 동작

    • 스트림 : 파일, 콘솔, 기타..
fin.get(ch);// 파일에서 한 글자 읽음, 그전에 \n 만나면 거기까지 출력
fin.getline(buf, 20); // 파일에서 문자 20개를 읽음, 그전에 \n 만나면 거기까지 출력
getline(fin, line); // 파일에서 한 줄을 읽음
fin >> word;// 파일에서 한 단어를 읽음

파일에서 '한 줄씩' 읽기

ifstream fin;
fin.open("HelloWorld.txt");

string line;

//eofbit : false
//failbit : false
while(!fin.eof())
{
  //마지막 라인을 읽고나서 eofbit 가 true로 설정됨

  getline(fin, line);//읽을떄 마다 파일 포인터가 읽은 문자열 다음위치를 가리킨다.
  cout << line << endl;
}

find.close();

빈 파일 읽기

//eofbit : false
//failbit : false
while(!fin.eof())
{
  //빈 파일이기에 line에 아무것도 안들어간다, 그러나 출력을 한다.
  getline(fin, line);
  cout << line << endl;
}

EX 1 - 파일에서 한 단어씩 읽기(문자열 하나, 숫자 하나)

//"ABCD 12000"

ifstream fin;
fin.open("HelloWorld.txt");

string name;
float balance;
while(!fin.eof())
{
  fin >> name >> balance
  cout << name << ": $" << balance << endl;
}

find.close();

EX 2 - 파일에서 한 단어씩 읽기(숫자만 있는 경우)

//"100 200 300"

ifstream fin;
fin.open("HelloWorld.txt");

float number;
while(!fin.eof())
{
  fin >> number;
  cout << number << endl;
}

find.close();

EX 3 - 숫자들과 뉴라인, 잘못된 읽기 문제 사례

//"100 200 300\n"

while(!fin.eof())
{
  // 300 읽고 난후 파일 포인터는 \n을 가리키고 있는 상태

  fin >> number;
  
  // \n 부터 한문자씩 차례대로 읽는 과정에서 eof가 있다는 것을 확인.
    // eof를 읽었기에 eofbit 설정
    // \n 도 공백과 같은 구분 문자로서 역할을 한다고 생각하면 된다.
  // \n 을 읽을때 숫자가 아니기에 failbit 설정된다.

  // number는 아까 읽었던 300을 한번더 출력
  cout << number << endl;
}

/*
OUTPUT >
  100
  200
  300
  300
*/

EX 4 - 잘못된 입력과 숫자들

//"100 C++ 300"

while(!fin.eof())
{
  // 100 읽고 난후 파일 포인터는 C++을 가리킴

  fin >> number;
  // C++를 읽으면서 파일포인터가 eof 에 도달하지 못함, 왜냐하면 뒤에 공백까지만 읽기 때문에
    // eof를 못 읽었기에 eofbit가 설정되지 않음
  // C++를 읽을때 숫자가 아니기에 failbit 설정된다.

  // number는 아까 읽었던 100을 한번더 출력
  cout << number << endl;
}

/*
OUTPUT >
  100
  100
  100
  100
  :
*/

고치는 방법

  • 시도 1 - 제대로 읽은 것만 출력
while(!fin.eof())
{
  fin >> number;
  if(!fin.fail())// 제대로 읽은 건지 출력
  {
    cout << number << endl;
  }
}

// 그러나 EX 4는 해결하지 못함, 왜?? 파일 포인터가 다음 문자열로 넘어가지 않으니까
  • 시도 2 - 다음 구분 문자까지 건너뛰기
while(!fin.eof())
{
  fin >> number;
  if(fin.fail()) // 제대로 못 읽은 경우 넘어가도록 처리
  {
    // 상태 원상복구
    fin.clear(); 
    // 다음 문자까지 파일 포인터 건너 뜀(다음 문자까지 구분은 ' '으로 지정)
    fin.ignore(LLONG_MAX, ' '); 
  }
  else
  {
    cout << number << endl;
  }
}

/*
그런데 \t와 스페이스 둘다 있는 경우는 제대로 동작하지 않음
"100 C++\t300" 이면 C++이후 300 문자열들을 다 버림, \t를 ' '으로 인식하지 않아서 
C++\t300채로 버린다.
*/
  • 숫자만 읽기
string trash;
while(!fin.eof())
{
  fin >> number;
  if(fin.fail())
  {
    fin.clear();
    // 숫자가 아닌 경우 문자열로 읽어버려 trash에다 버리게 된다.
    fin >> trash; 
  }
  else
  {
    cout << number << endl;
  }
}
  • 주의 해야할 부분
string trash;
while(!fin.eof())
{
  fin >> number;
  if(fin.fail())
  {
    //"100 200 C++"
    //fin.clear() 위치가 바뀌면 문제가 된다.
    

    fin >> trash;
    // C++ 문자열 읽은 다음에 failbit가 설정되어서 파일 포인터가 다음으로 넘어가질 못한다. 
    // 즉, fin >> 이 정상 동작을 못한다.
    
    //fail bit를 초기화 하기위해 여기서 clear를 하면
    //eof bit도 같이 초기화 되어 더 입력을 받기위해 대기
    fin.clear();
  }
  else
  {
    cout << number << endl;
  }
}
  • 파일 테스트 케이스
    1. 유효한 입력 => EOF
      : 100\n'200EOF'
    2. 유효한 입력 => '\n' => EOF
      : 100\n'200\nEOF'
    3. 유효하지 않은 입력 => EOF
      : 100\n'C++EOF'
    4. 유효하지 않은 입력 => '\n' => EOF
      : 100\n'C++\nEOF'
    5. 공백: 위의 과정을 공백과 탭도 포함 할 건가?
      : 100' '200'\t'EOF
    6. 키보드 입력과 입력 리다이렉션을 둘 다 확인할 것
      : 100 200 300
      : cmd>numbers.exe < numberinput.txt

File Write

  • C
FILE* fp;
fp = fopen("HelloWorld", "w");

char line[512];
if(fgets(line, 512, stdin) != NULL)
{
  fprintf(fp, "%s\n", line);
}

fclose(fp);
  • C++
ofstream fout;
fout.open("HellowWorld.txt");

string line;
getline(cin, line);
if(!cin.fail())
{
  fout << line << endl;
    // "endl은 \n 과 조금 다르다"
    // "enel은 뉴 라인 한 다음에 버퍼를 플러시를 한다."
    // "fluah()라는 함수가 있다."
    // "* 단 C에서는 \n을 만나면 플러시를 한다.	"
}

fin.close();
  • put()

    • 문자를 써 넣음
    • fout.put(ch);
  • <<

    • fout << line << endl;

바이너리 파일 읽기

  • C
FILE* fp;
fp = fopen("studentRecord.dat", "rb");
if(fp != NULL)
{
  Record record;
  fread(&record, sizeof(Record), 1(1개 읽어라), fp);
}

fclose(fp);
  • C++
ifstream fin("studentRecord.dat", ios_bas::in | ios_base::binary);

if(fin.is_open())
{
  Recordd record;
  fin.read((char*)&record, sizeof(Record));
}

fin.close();
  • read(char*, streamsize)
    • 파일로 부터 문자 20개를 firstName에 저장
    • fin.read(&firstName, 20);

바이너리 파일에 쓰기

  • C
FILE* fp;
fp = fopen("studentRecord.dat", "w");
if(fp != NULL)
{
  char buffer[2] = "Pope kim";
  fwrite(buffer, 20, 1(1번 써라), fp)
}

fclose(fp);
  • C++
ofstream fout("studentRecords.dat", ios_base::out | ios_base::binary);

if(fout.is_open())
{
  char buffer[20] = "Pope kim";
  fout.write(buffer, 20);
}

fout.close();
  • write(const char*, streamsize)
    • fout.write(firstName, 20)
    • firstName에 저장 되어있는 문자 20자를 파일에 씀

파일 안에서의 탐색

  • C
FILE* fp;
fp = fopen("studentRecords.dat", "w");

if(fp != NULL)
{
  if(fseek(fp, 20, SEEK_SET) == 0)
  {
    // 21번째 위치에서부터 덮어쓰기
  }
}
  • C++
fstream fs("HellowWorld.dat", ios_base::in | ios_bse::out | ios_base::binary)

if(fs.is_open())
{
  fs.seekp(20, ios_base::beg);//시작부터 20번째
  if(!fs.fail())
  {
    // 21번째 위치에서부터 덮어쓰기
  }
}
  • 탐색(seek) 유형

    • 절대적

      • 특정한 위치로 감
      • 보통 tellp()/tellg()를 사용해서 기억해 놨던 위치로 돌아갈 때 사용
    • 상대적

      • ios_base::beg : 시작부터
      • ios_base::cur : 현재부터
      • ios_base::end : 끝에서부터
      • 파일의 끝에서부터 5바이트 앞의 위치로 이동
    • 파일 [쓰기] 위치 읽기 및 변경

      • tellp():쓰기 포인터의 위치를 구함

        • ios::pos_type pos = fout.tellp();
      • seekp()

        • 절대적 : fout.seekp(0)
          • 처음 위치로 이동
        • 상대적 : fout.seekp(20, ios_base::cur);
          • 현재 위치로부터 20 바이트 뒤로 이동
    • 파일 [읽기] 위치 읽기 및 변경

      • tellg():읽기 포인터의 위치를 구함

        • ios::pos_type pos = fout.tellg();
      • seekg()

        • 절대적 : fout.seekg(0)
          • 처음 위치로 이동
        • 상대적 : fout.seekg(-10, ios_base::end);
          파일의 끝에서부터 10 바이트 앞으로 이동
    • text.txt

    1234567890
    1234567890[ ]
    |          |
    beg		  end  
  • EX

fstream fs;
fs.open("test.txt");

fs.seekg(0, ios_base::end);
fs << "abc";

fs.close();

=> 1234567890abc
  • EX
fs.seekg(0, ios_base::beg);
=> abc4567890
- EX
fs.seekg(4, ios_base::beg);
=> 1234abc890

beg에서부터 4칸 이동
  • EX
fs.seekg(4, ios_base::end);
=> 1234567890    abc
  • EX
fs.seekg(-4, ios_base::end);
=> 123456abc0
  • EX
fstream fs;
fs.open("test.txt", ios_base::app);

cout << fs.tellg() << endl;
  => 0
fs << "abc";

cout << fs.tellg() << endl;
  => 13

fs.close();

=> 123456789abc
  • EX
fstream fs;
fs.open("test.txt", ios_base::app | ios_base::ate);

cout << fs.tellg() << endl;
  => 10
fs << "abc";

cout << fs.tellg() << endl;
  => 13

fs.close();

=> 123456789abc

기타 정보

  • 를 getline()과 같이 쓰지 말 것

// 입력:1 \n
// Cheerios

cin >> number; // 1
getline(cin, line);// 위에서 \n이 버퍼에 남아서 line에 \n 넣고 넘어가버림.
  • 어떻게 고치나?
cin >> number;
cin >> ws;			==> \n을 버림
getline(cin, line);
profile
mohadang

0개의 댓글