Index
- stdstring 클래스
- strstring이 좋은가?
- 파일입출력(I/O)
- 파일 읽기 한 문자, 한 줄, 한 단어
- 파일 읽기 Best Practice
- 파일 쓰기, 바이너리 파일 읽기, 파일 안에서의 탐색
stdstring 클래스
string을 사용하지 않을 경우
char line[256];
cin.getline(line, 256);
- 위 코드는 아래 두 경우에 작동하지 않음
- 아무것도 읽지 못했을 때
- 버퍼가 충분히 크지 않을 때
- 위 경우에서는 한 줄에 문자가 256자 이상일 때
std::string 클래스
-
std::string
클래스를 이용한 문자열은 길이가 증가할 수 있음#include<string> std::string firstName; std::cin >> firstName;
대입(Assignment)과 덧붙이기(Appending);
C의 경우
char firstName[20] = "POPE";
char fullName[20];
//대입 - 안전하지 않음
strcpy(fullName, firstName);
//덧붙이기 - 안전하지 않음
strcat(fullName," KIM");
C++의 경우
string firstName = "POPE";
string fullName = "John Doe";
//대입
fullName = firstName;
//덧붙이기
fullName += " KIM";
문자열 합치기(Concatenation)
C의 경우
char firstName[20] = "POPE";
char lastName[20] = "KIM";
char fullName[40];
snprintf(fullName, 40, "%s %s", firstName, lastName);
C++의 경우
string firstName[20] = "POPE";
string lastName[20] = "KIM";
string fullName[40];
fullName = firstName + " " + lastName;
비교(Relational)연산자
C의 경우
if (strcmp(firstName1, firstName2) == 0)
{
//...
}
if (strcmp(firstName1, firstName2) > 0)
{
//...
}
C++의 경우
if(firstNam1 == firstName2)
{
//...
}
//사전 상의 순서를 비교
if(firstName1 > firstName2)
{
//...
}
size(), length(), c_str()
-
size()
,length()
-
문자열의 길이를 반환(주로
size()
를 사용)cout << firstName.size() <<endl; cout << firstName.length() << endl;
-
-
c_str()
-
const char*
-
해당 string이 가지고 있는 문자 배열의 시작 주소를 가리키는 포인터를 반환
string line; cin >> line; const char* cLine = line.c_str();
-
string 속의 한 문자에 접근하기
-
C와 같음
string firstName = "POKE"; char letter = firstName[1]; firstName[2] = 'P';
-
한 줄 읽기
string mailHeader; //'\n'문자를 만날 때까지 cin에서 문자들을 꺼내서 mailHeader에 저장 getline(cin, mailHeader); //'@'문자를 만날 때까지 cin에서 문자들을 꺼내서 mailHeader에 저장 getline(cin, mailHeader, '@');
-
다음의 조건을 만족할 때까지 계속해서 스트림에서 문자열을 꺼내 string에 저장
- end-of-file을 만날 때 (eofbit값이 true가 됨)
- 구분 문자(deliminiter)를 만날 때까지(구분문자는 버려짐)
strstring이 좋은가?
<sstring>
std::istringstream
cin
과 비슷 : 키보드 대신 string으로부터 읽어옴sscanf()
와 비슷
std::ostringstream
cout
과 비슷 : 콘솔 대신 string에 출력함sprintf()
와 비슷
- 허나 그렇게 자주 쓰이지는 않음
C헤더를 써도 될까?
-
YES!!
-
협업의 C++어플리케이션에서는 여전히 성능상의 이유로 많은 C함수들을 사용
-
“이건 C++스럽지 않아서 틀려”라고 하는 사람은 그냥 무시할 것
C C++ <cstring>
<cstdio>
<cctype>
그래서 string이 더 낫다는 건가?
장점
- 문자 배열 길이에 관해 고민할 필요가 없음
단점
- heap메모리 할당은 느림
- 메모리 단편화 문제
- 내부 버퍼의 증가는 멀티 스레드 환경에서 안전하지 않을 수도 있음
- 여전히 c++를 쓰는 업계가 어디인지 생각 할 것 (성능 중시)
결론
여전히 sprintf와 함께 char[]를 매우 많이 사용한다.
파일입출력(I/O)
ifstream
- 파일입력
ofstream
- 파일출력
fstream
- 파일 입력 및 출력
파일 열기
C의 경우
FILE* fp;
//읽기 전용으로 파일 오픈
fp = fopen("helloworld.txt","r");
//쓰기 전용으로 파일 오픈(파일이 없으면 생성)
fp = fopen("helloworld.txt","w+");
//일기와 쓰기 범용으로 파일 오픈
fp = fopen("helloworld.txt","r+");
C++의 경우
//읽기 전용으로 파일을 오픈
ifstream fin;
fin.open("helloworld.txt");
//쓰기 전용으로 파일을 오픈(파일이 없으면 생성)
ofstream fout;
fout.open("helloworld.txt");
//읽기와 쓰기 범용으로 파일을 오픈
fstream fs;
fs.open("helloworld.txt");
open()
-
각 스트림마다
open()
메서드가 있음fin.open("HelloWorld.txt",ios_base::in|ios_base::binary)
-
모드 플래그(mode flag)
-
네임스페이스
- ios_base
-
모든 조합이 유효하지는 않음
-
모드 플래그
in
,out
,ate
,app
,trunc
,binary
-
-
파일 열기 모드의 예
C | C++ |
---|---|
“r” | ios_base::in |
“w” | ios_base::out 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의 경우
FILE *fp;
//...
fclose(fp);
C++의 경우
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())
{
//...
}
파일 읽기 한 문자, 한 줄, 한 단어
파일에서 문자 하나씩 읽기
C의 경우
FILE* fp;
fp= fopen("HelloWorld.txt","r");
char character;
do
{
charactor = getc(fp);
printf("%c", character);
} while(character != EOF);
fclose(fp);
C++의 경우
ifstream fin;
fin.open("HelloWorld.txt");
char character;
while(true)
{
fin.get (character);
if(fin.faile()) break;
cout<<character;
}
fin.close();
get(), getline(), »
-
어떤 스트림(예를들어 cin, instringstream)을 넣어도 동일하게 동작
fin.get(character); fin.getline(firstName, 20); //파일에서 문자 20개를 읽음 getline(fin, line); //파일에서 한 줄을 읽음 fin>>word; //파일에서 한 단어를 읽음
파일에서 한 줄씩 읽기
ifsteam fin;
fin.open("Hello World.txt");
string line;
while(!fin.eof())
{
getline(fin,line);
cout<<line<<endl;
}
fin.close();
파일에서 한 단어씩 읽기
ifstream fin;
fin.open("Hello World.txt");
string name;
float balance;
while(!fin.eof())
{
fin >> name >> balance;
cout << name << ":$" << balance << endl;
}
fin.close();
잘못된 파일 입력을 처리하는 방법
-
숫자만 입력을 받길 원할 경우의 예외처리
while(!fin.eof()) { fin >> number; if(fin.fail()) { //다음 구분 문자까지 건너뛰기 fin.claer(); fin.ignore(LLONG_MAX,' '); } else { cout<<number<<endl; } }
- 위의 코드 경우
\t
와 같은 구분문자는 구분하지 못한다는 문제점이 존재한다.
ifstream fin; fin.open("HelloWorld.txt"); int number; string trash; while(!fin.eof()) { fin>>number; if(fin.fail()) { fin.claer(); fin>>trash; } else { cout<<number<<endl; } } fin.close();
- 제대로 동작함
- 위의 코드 경우
파일 읽기 Best Practice
EOF처리는 까다롭다
- 입출력 연산이 스트림 상태 비트를 변형한다는 사실을 기억할 것
- EOF를 잘못 처리하면 무낳 반복을 초래
- clear()쓸 때는 두번 생각하자
베스트 프랙티스
- 입력 처리 문제는 업계에서 매우 흔한 문제
- C#이나 Java를 본떠 자신만의 스트림리더를 만드는 일이 흔함
- 처음부터 완벽하게 입력을 처리하는 코드를 작성하는 건 거의 불가능
훌륭한 테스트 케이스
-
유효한 입력 뒤에 EOF
0 1 2 3 4 5 6 7 1 0 0 \n 2 0 0 EOF -
유효한 입력과 뉴라인
\n
뒤에 EOF0 1 2 3 4 5 6 7 8 1 0 0 \n 2 0 0 \n EOF -
유효하지 않은 입력 뒤에 EOF
0 1 2 3 4 5 6 7 1 0 0 \n C + + EOF -
유효하지 않은 입력과 뉴라인
\n
뒤에 EOF0 1 2 3 4 5 6 7 8 1 0 0 \n C + + \n EOF -
공백:탭도 포함할 것인가
0 1 2 3 4 5 6 7 8 1 0 0 C + + \t … -
키보드 입력과 리다이렉선(redirection)을 둘 다 확인할 서)
- 100 200 300
- cmd>numbers.exe<numbinput.txt
파일 쓰기, 바이너리 파일 읽기, 파일 안에서의 탐색
파일에 쓰기
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("HelloWorld.txt");
string line;
getline(cin,line);
if(!cin.fail())
{
fout<<line<<endl;
}
fin.close();
put(), «
-
put()
-
문자를 써 넣음
fout.put(character);
-
-
«
fout<<line<<end;
바이너리 파일 읽기
C의 경우
FILE *fp;
fp = fopen("studentRecord.dat","rb");
if(fp != NULL)
{
//record는 char[20]변수 2개와 int변수 1개를 가짐
Record record;
fread(&record, sizeof(Record),1,fp);
}
fclose(fp);
C++의 경우
ifstream fin("studentRecords.dat",ios_base::in | ios_base::binary);
if(fin.is_open())
{
Record record;
fin.read((char*)&record,sizeof(Record));
}
fin.close();
ifstream::read()
-
read(char*, streamsize)
//파일로부터 문자 20자를 읽어 firstName에 저장 fin.read(&firstName, 20);
바이너리 파일에 쓰기
C의 경우
FILE *fp;
fp = fopen("studentRecords.dat", "w");
if(fp != NULL)
{
char buffer[20] = "Pope Kim";
fwrite(buffer, 20, 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();
ofstream::write()
-
write(const char*, streamsize)
//firstName에 저장되어 있는 문자 20자를 파일에 씀 fout.write(firstName,20);
파일 안에서의 탐색
C의 경우
FILE* fp;
fp = fopen("studentRecord.dat", "w");
if(fp != NULL)
{
if (fseek(fp.20,SEEK_SET) == 0)
{
// 21번째 위치에서부터 덮어쓰기
}
}
fclose(fp);
C++의 경우
fstream fs("helloWorld.dat", ios_base::in | ios_base::out | ios_base::binary);
if (fs.is_open())
{
fs.seekp(20, ios_base::beg);
if (!fs.fail())
{
// 21번째 위치에서부터 덮어쓰기
}
}
fs.close();
탐색 유형
-
절대적
- 예) 특정한 위치로 감
- 보통
tellp()
,tellg()
를 사용해서 기억해놨던 위치로 돌아갈 때 사용
-
상대적
-
예) 파일 끝에서 5바이트 앞의 위치로 이동 등..
탐색 방향 유형 ios_base::beg
ios_base::cur
ios_base::end
-
파일 쓰기 위치 읽기 및 변경
-
tellp()
쓰기 포인터의 위치를 구함ios::pos_type pos = fout.tellp();
-
tellg()
읽기 포인터의 위치를 구함ios::pos_type pose = fin.tellg();
-
seekp()
쓰기 포인터의 위치를 변경함절대적
fout.seekp(0); // 처음 위치로 이동
상대적
// 현재 위치로부터 20바이트 뒤로 이동 fout.seekp(20, ios_base::cur);
-
seekg()
읽기 포인터의 위치를 변경함절대적
fin.seekg(0); // 처음 위치로 이동
상대적
// 파일의 끝에서부터 10 바이트 앞으로 이동 fin.seekg(-15, ios_base::end);
기타 정보
>>
를getline()
과 같이 쓰지 말 것
cin >> number; getline(cin, line); // cin에서 공백을 꺼내 감
해결법
cin >> number; cin >> ws; //공백을 버림 getline(cin,line);