배움 저장소

[홍정모의 따라하며 배우는 C++] 18. 입력과 출력 본문

Programming Language/C++

[홍정모의 따라하며 배우는 C++] 18. 입력과 출력

시옷지읏 2022. 1. 5. 23:40

printf vs cout

 ㄴprintf: c-style 입출력은 문자열과 파일을 구별한다. fprintf 함수처럼 별도의 함수를 사용한다.

 ㄴcout: c++은 cout으로 입출력을 모두 사용할 수 있어 편리하다.

18.1 istream으로 입력받기


cin은 문자열에서도 사용하지만 파일 입출력에서 유용하게 활용할 수 있다. 파일 읽기를 위해서 공부해보자

 

stream은 buffer에 저장된 자료를 꺼내오는 방식으로 동작한다.

- 아래 예제를 실행해보자. 입력 문자 개수가 5개가 넘어가면 런타임 에러가 발생한다.

char buf[5];
cin >> buf;
cout << buf;

 

<iomanip>의 setw 사용해보기

- setw를 사용하면 최대 입력개수까지만 해당 메모리에 저장하고 나머지는 버퍼에 남겨둔다. 아래에서 5글자 이상의 입력값을 주면 그 다음 입력을 받지 않고 출력함을 확인할 수 있다.

char buf[5];

cin >> setw(5) >> buf;
cout << buf << endl;

cin >> setw(5) >> buf;
cout << buf << endl;
입력: 1234567

출력:
1234
567

 

while 반복문과 cin을 사용하여 입력값을 받을 수 있다.

- 이 때 space는 생략된다. 숫자 입력값을 구분하여 받을 때 편하다

int i;
float f;
while (cin >> i >> f)
    cout << i << " " << f;
입력: 123 12.3
출력: 123 12.3

string을 입력받을 때 생략되는 space는 문제가 될 수 있다.

char ch;
while (cin >> ch)
    cout << ch;
입력: Hello World
출력: HelloWorld

 

cin의 멤버함수 get을 사용하면 space 입력값을 받을 수 있다

char ch;
while (cin.get(ch))
    cout << ch;
입력 : hello world
출력 : hello world

 

cin 인자에 입력받을 최대 개수를 지정할 수 있다

- 최대 개수를 넘어가는 입력은 내부 buffer에 저장하고 있다가 그 다음 입력받을 때 밀어넣는다.

char buff[5];
cin.get(buff,5);
cout << buff << endl;

cin.get(buff, 5);
cout << buff << endl;
입력: hello world

출력:
hell
o wo

 

cin의 멤버함수 getline: getline은 한 줄씩 읽는다. 최대 입력 값을 넘어가면 저장하지 않고 버린다

cin의 멤버함수 gcount: 받은 입력값의 개수를 반환한다.

char buff[5];
cin.getline(buff,5);
cout << cin.gcount() << " " << buff << endl;

cin.getline(buff, 5);
cout << cin.gcount() << " " << buff << endl;
입력 :hello world

출력:
4 hell
0

위에서 hello world를 입력하자 hell만 출력되었다. o와 null character는 버려졌다.

getline은 줄바꿈 character ('\n')까지 읽어들이되 끝에 null character를 추가한다

입력: 123
출력
4 123

 

cin의 멤버함수 ignore : ignore는 인자만큼의 입력값을 무시한다

char buff[5];
cin.ignore(2);

cin >> buff;
cout << cin.gcount() << " " << buff << endl;
입력: 1234
출력: 2 34

 

cin의 멤버함수 peek : peek은 첫 입력값을 버퍼에서 꺼내지 않고 출력시켜준다

char buff[5];

cout << (char)cin.peek() << endl;
cin >> buff;
cout << cin.gcount() << " " << buff << endl;

 

cin의 멤버함수 unget : unget은 마지막 출력값을 다시 버퍼에 넣는다.

char buff[5];
cin >> buff;
cout << buff << endl;

cin.unget();
cin >> buff;
cout << buff << endl;
입력: 1234
출력:
1234
4

 

cin의 멤버함수 putback : putback은 해당 값을 buffer에 밀어넣는다

char buff[5];
cin >> buff;
cout << buff << endl;

cin.putback('A');
cin >> buff;
cout << buff << endl;
입력:123

출력:
123
A

 

cin과 string을 함께 사용하기

string buff;
getline(cin, buff);
cout << cin.gcount() << " " << buff << endl;
입력: 123

출력:
>> 1
>> 0 123

 

활용해보기: putback을 사용하면 커서가 앞으로가며 입력값을 밀어넣어준다.

char buf[10];

std::cout << (char)std::cin.peek() << std::endl;
std::cin.ignore(1);
std::cin >> buf;
std::cout << buf << std::endl;

std::cin.unget();
std::cin.putback('A');
std::cin.putback('B');
std::cin.putback('C');
std::cin >> buf;
std::cout << buf << std::endl;
입력:123456
출력:
1
23456
CBA6

 

18.2 ostream으로 출력하기


출력값에 영향을 주는 다양한 flag를 알아보자

 

숫자 출력값의 형식을 설정하는 flags

 ㄴ setf, unsetf

 ㄴ std::ios::{ showpos, dec, uppercase, basefield }

cout.setf(std::ios::showpos); // set to show positive sign
cout << 111 << endl;  // >> +111

cout.unsetf(std::ios::showpos); //un set
cout << 111 << endl;  // >> 111

cout.unsetf(std::ios::dec); // unset decimal
cout.setf(std::ios::hex);   // set hexa decimal
cout << 111 << endl;  // >> 6f
cout.setf(std::ios::uppercase); // set uppercase alphabet
cout << 111 << endl;  // >> 6F

cout << std::nouppercase; // #include <iomanip>
cout << 111 << endl;  // >> 6f

cout.setf(std::ios::oct, std::ios::basefield); // work without unset
cout << 111 << endl;  // >> 157

 

출력값의 위치를 조정하는 flags

ㄴsetw: 인자만큼의 자리수를 사용하여 정렬한다. left, right, internal을 사용할 수 있다.

ㄴfill: 빈 자리를 해당 문자로 채워준다

cout << -123 << endl;                                 // >> -123
cout << std::setw(6) << std::left << -123 << endl;    // >> - 123
cout << std::setw(6) << std::right<< -123 << endl;    // >>  -123
cout << std::setw(6) << std::internal << -123 << endl;// >> -  123

cout.fill('*');
cout << std::setw(6) << std::left << -123 << endl;    // >> -123**
cout << std::setw(6) << std::right << -123 << endl;   // >> **-123
cout << std::setw(6) << std::internal << -123 << endl;// >> -**123

 

소숫점 자리수 설정에 사용하는 flags

ㄴ fixed: 해당 자리수만큼 0을 채워서 표시

//cout << std::defaultfloat; // set default float 
cout << std::setprecision(0) << 1.23 << endl;// >> 1
cout << std::setprecision(1) << 1.23 << endl;// >> 1
cout << std::setprecision(2) << 1.23 << endl;// >> 1.2
cout << std::setprecision(3) << 1.23 << endl;// >> 1.23

cout << endl;
cout << std::fixed;
cout << std::setprecision(0) << 1.23 << endl;// >> 1
cout << std::setprecision(1) << 1.23 << endl;// >> 1.2
cout << std::setprecision(2) << 1.23 << endl;// >> 1.23
cout << std::setprecision(3) << 1.23 << endl;// >> 1.230

cout << endl;
cout << std::scientific;
cout << std::setprecision(0) << 1.23 << endl;// >> 1e+00
cout << std::setprecision(1) << 1.23 << endl;// >> 1.2e+00

cout << endl;
cout << std::scientific << std::uppercase;
cout << std::setprecision(0) << 1.23 << endl;// >> 1E+00
cout << std::setprecision(1) << 1.23 << endl;// >> 1.2E+00

 

 showpoint: 소수점을 생략하지 않고 표시

cout << std::boolalpha;
cout << true << false<< endl; // >> truefalse

cout << endl;
cout << std::setprecision(2) << 1.0 << endl;// >> 1
cout << std::showpoint;
cout << std::setprecision(2) << 1.0 << endl;// >> 1.0
cout << std::noshowpoint;

 

 

18.3 문자열 스트림


iostream, string stream 모두 basic stream을 상속받는다. iostream의 형제인 string stream을 사용해보자

- string stream을 사용하기 위해 <sstream>을 포함하자

- 연산자 <<입력받은 문자열을 더한다. endl을 넣으면 줄바꿈 문자도 해당 문자열에 추가된다

- 멤버함수 str( )은 입출력 모두 사용할 수 있다. buffer에 있는 값을 모두 대체하거나 모든 값을 가져온다.

stringstream oss; // output string stream
oss << "Hell"; // "<<" : insertion operator, add to buffer
oss << "o World";
    
string str;
oss >> str;           // ">>" : extraction operator, until ' ', '\n'
cout << str << endl;  // >> Hello
    
oss.str("Hello World"); // replace all buffer to args
str = oss.str();        // take every buffer to str
cout << str << endl;  // >> Hello World
// cout << oss.str() << endl;

 

string stream의 << 연산자는 space를 기준으로 값을 구분하여 입력받는다

- int, double 자료형을 string으로 받았다.

int i = 123;
double d = 456.7;

oss << i << " " << d;
string num1; // int num1;
string num2; // dobule num2;
oss >> num1 >> num2;

cout << num1 << "|" << num2 << endl; // >> 123|456.7

신기하게도 string을 각 자료형에 맞게 넣어줄 수 있다.

stringstream oss;

oss << "123 456.7";
int num1;
double num2;
oss >> num1 >> num2;

cout << num1 << "|" << num2 << endl; // >> 123|456.7

 

string stream 비우기

- clear 멤버함수는 상태를 초기화한다. state(상태)는 아래에서 다룬다

oss.str("");      // replace buffer to empty
oss.str(string());// replace buffer to empty
os.clear(); // set state to default

 

18.4 흐름 상태와 입력 유효성 검증

stream states and input validatation


stream states(흐름 상태) 확인하기

- stream state로 입력값이 의도한대로인지 확인할 수 있다

 

ios소개 https://www.cplusplus.com/reference/ios/

 

<ios> - C++ Reference

header Input-Output base classes Header providing base classes and types for the IOStream hierarchy of classes: Types Class templates basic_iosBase class for streams (type-dependent components) (class template )fposStream position class template (class tem

www.cplusplus.com

 

stream 멤버함수를 사용하여 state를 확인할 수 있다. 각 멤버함수는 bool 자료형의 결과값을 반환한다. <iostream>

// ios: iostream, useful for both file stream and iostream
void printState(const std::ios& stream) {
    cout << boolalpha;
    cout << "good()=" << stream.good() << endl;
    cout << "bad()=" << stream.bad() << endl;
    cout << "fail()=" << stream.fail() << endl; // !good
    cout << "eof()=" << stream.eof() << endl; // read untill end?
}

 

cin의 멤버함수 rdstate는 state를 bit 형태로 출력할 수도 있다. readstate의 줄임이다.

while (true) {
    int i;
    cin >> i; // check is input integer? not character

    printState(cin); // cin has state saves that input matching each when digit or str

    cout << boolalpha;
    cout << bitset<8>(cin.rdstate()) << endl; // readstate has bit info
    
    cout << bitset<8>(istream::goodbit) << endl; // goodbit mask
    cout << bitset<8>(istream::failbit) << endl; // failbit mask
        
    // use bitmask to check wheter the state fail
    cout << !bool((cin.rdstate() & istream::failbit) != 0) << endl;
    cout << (cin.rdstate() != istream::goodbit) << endl;
    
    cin.clear();
    cin.ignore(1024, '\n');
    cout << endl;
}

- istream::goodbit의 비트마스크 출력값을 확인해보자. 0000 0000이다. goodbit의 비트마스크로 해당 상태의 good을 확인할 수 없다. istream::goodbit값을 비트마스크로 확인하려면 istream::failbit을 이용해야 한다. 

- 신기한건 cin.rdstate() == istream::goodbit은 별 문제 없이 잘 작동한다

입력: 12

출력:
good()=true
bad()=false
fail()=false
eof()=false
00000000      <- rdstate
00000000      <- goodbit
00000010      <- failbit
true
true

 

Input Validatation(입력 유효성) 검증

cctype 소개 https://www.cplusplus.com/reference/cctype/

 

<cctype> (ctype.h) - C++ Reference

header (ctype.h) Character handling functions This header declares a set of functions to classify and transform individual characters. Functions These functions take the int equivalent of one character as parameter and return an int that can either be anot

www.cplusplus.com

 

입력값의 종류 확인해보기 <cctype>

-  이 때 cin은 space로 입력값을 구분하여 받기 때문에 입력된 값에 blank는 없다. isblank는 항상 false이다.

void printCharacterClassification(const int& i) {
    cout << boolalpha;       // return integer
    cout << "isalnum " << bool(std::isalnum(i)) << endl; // alphabet-number
    cout << "isblank " << bool(std::isblank(i)) << endl;
    cout << "iscntrl " << bool(std::iscntrl(i)) << endl;
    cout << "isdigit " << bool(std::isdigit(i)) << endl;
    cout << "islower " << bool(std::islower(i)) << endl;
    cout << "isupper " << bool(std::isupper(i)) << endl;
}
while (true) {
    char ch;
    cin >> ch; // check is input integer? not character

    printState(cin); // cin has state that saves input is matched to type
        
    printCharacterClassification(ch);

    cin.clear();
    cin.ignore(1024, '\n');
    cout << endl;
}
입력:a

출력:
good()=true
bad()=false
fail()=false
eof()=false
isalnum true
isblank false
iscntrl false
isdigit false
islower true
isupper false

 이 때 isalnum함수는 ASCII 코드 내에 정해진 숫자와 문자를 구분한다. 만약 정수 형태로 입력값을 넣으면 해당 정수와 짝지어진 ASCII문자가 숫자인지 문자인지를 판별한다. 이와 같이 cctype 헤더파일의 함수들은 모드 ASCII 코드내 문자를 다룬다.

 

입력값이 모두 숫자인지 확인하기

bool isAllDigit(const string& str) {
    bool perfectDigit = true;
    for (auto e : str) {
        if (!std::isdigit(e)) {
            perfectDigit = false;
            break;
        }
    }
    return perfectDigit;
}

int main()
{
    cout << boolalpha;
    cout << isAllDigit("1234") << endl; // >> ture
    cout << isAllDigit("123a5")<< endl; // >> false
}

 

18.5 정규 표현식 소개

regular expression


정규 표현식을 사용하여 문자열이 지정한 형식과 동일한지 확인할 수 있다

 

<regex>를 포함시켜주자

- regex 클래스를 사용하여 정규식을 설정하자. \d는 한 글자 숫자 형식을 가진다. 아래 코드를 실행시켜 보자.

#include <iostream>
#include <regex>
using namespace std;

int main()
{
    regex reg("\\d"); // "\d" => "\\d" Only one digit
    string str;

    while (true) {
        getline(cin, str);
        if (std::regex_match(str, reg))// check regex
            cout << "Match: ";
        else
            cout << "Diff: ";
        
        auto begin = std::sregex_iterator(str.begin(), str.end(), reg);
        auto end= std::sregex_iterator();
        for (auto itr = begin; itr != end; ++itr) {
            smatch match = *itr;
            cout << match.str() << " ";
        }
        cout << endl;
    }
    cout << endl;
}
입력: 5
출력: Match: 5

입력: 123
출력: Diff: 1 2 3

입력: 12as34
출력: Diff: 1 2 3 4

 

다양한 정규식을 활용해보자

ㄴ+ 해당 문자가 하나이상 포함되어야 한다.

ㄴ*  해당 문자가 하나도 없거나 그 이상 있어도 된다.

ㄴ?  아무 문자가 하나도 없거나 그 이상 있어도 된다.

expression matches Not matches
a+b ab, aaab b, baa
a*b b, ab, aaab daa
.*cat cat, 9393cat,
the old cat, c7sb@#puiercat
dog
a[n]? h a herb, an herb
ann hat
//regex reg("\\d+");   // "\d" => "\\d" Only one digit
//regex reg("[ab]");   // Only a or Only b. not a and b
//regex reg("[ab]+");  // any combination of a and b is OK
//regex reg("[ab]{3}");// Just Three any of a or b

//regex reg("[A-Z]+"); // any upper alpha, more than one
//regex reg("[A-Z]{3}"); // any upper alpha, only three sequence
//regex reg("[A-Z]{1,5}"); // any upper alpha, min one max five
regex reg("([0-9]{6})([-]?)([0-9]{7})"); // resident number format

 

https://www.cplusplus.com/reference/regex/ECMAScript/

 

ECMAScript syntax - C++ Reference

syntax specifications std::ECMAScript syntax ECMAScript regular expressions pattern syntax The following syntax is used to construct regex objects (or assign) that have selected ECMAScript as its grammar. A regular expression pattern is formed by a sequenc

www.cplusplus.com

 

18.6 기본적인 파일 입출력


input file stream과 output file stream을 동시에 사용하면 의도하지 않은 결과가 나타난다.

하나를 사용하고 만드시 close 멤버함수를 호출하거나 지역 변수로 ifstream과 ofstream을 사용하자

 

파일 쓰기 ASCII

- <fstream>을 포함하자

- output file stream을 사용하여 파일을 만들 수 있다. cout과 동일한 형태로 사용할 수 있어 편리하다.

- <<연산자를 사용하여 문자열을 넣으면 txt 파일에서 읽을 수 있다.

// writing
ofstream outputFS("test.dat"); // ios::app for append mode
// overriding constructor, open, create, replace....

if (!outputFS){
    cerr << "There is no File";
    exit(1);
}

// it's possible to read this .dat file with txt
outputFS << "Line 1" << endl; // save ASCII format
outputFS << "Line 2" << endl; // save ASCII format 
outputFS.close(); // or you can use Scope and local var

 

파일 읽기 ASCII

- input file stream을 활용하여 위에서 작성한 파일을 읽어보자

//ifstream inputFS("test.dat", ios::binary); //read binary
ifstream inputFS("test.dat"); // read ASCII

if (!inputFS) {
    cerr << "There is no File" << endl;
    exit(1);
}

while (inputFS) {
    string str;
    getline(inputFS, str);

    cout << str << endl;
}

 

 

파일에 string 쓰기

- 모든 정보를 string에 넣고 파일에 입력할 때가 있다. 구현해보자

ofstream OFS("stringstream.dat");
OFS << "Line1" << endl;
OFS << "Line2" << endl;
OFS.close(); // use scope

파일에 string 읽기

ifstream IFS("stringstream.dat");
string s;
while (IFS) {
    string str;
    getline(IFS, str);
    cout << str << endl;
}
IFS.close(); // use scope

 

Binary로 파일 읽고 쓰기

파일 쓰기 Binary

- Binary 파일은 데이터를 구분할 수 없기 때문에 저장 방식을 미리 알려야 한다. char 자료형을 사용하고 첫 데이터에 저장되는 데이터 개수를 알린다.

ofstream OFS("test.txt", ofstream::binary); // ios::app for append
// overriding constructor, open, create, replace....

if (!OFS){
    cerr << "There is no File";
    exit(1);
}

const unsigned length= 10; // number of data
OFS.write((char*)&length, sizeof(length)); // save count of data

for (int i = 0; i < length; ++i) {
    OFS.write((char*)&i, sizeof(i)); // save data
}
OFS.close();

파일 읽기 Binary

//ifstream IFS("test.dat", ios::binary); //read binary
ifstream IFS("test.txt", ifstream::binary); // read ASCII

if (!IFS) {
    cerr << "There is no File" << endl;
    exit(1);
}

unsigned len = 0;
IFS.read((char*)&len, sizeof(len));// check count

for (unsigned i = 0; i < len; ++i) {
    int num;
    IFS.read((char*)&num, sizeof(num));
    cout << num << endl;
}
IFS.close();

 

18.7 파일의 임의 위치 접근하기


임의 접근을 구현하기 위하여 파일을 새로 쓰자

// write file
const string filename = "randomaccess.txt";
{
    ofstream OFS(filename);

    // write a to z
    for(char i = 'a'; i<='z'; ++i)
        OFS << i;
    OFS << endl;
}

 

임의접근은 구현하기

- seekg 함수를 사용하여 원하는 byte만큼 이동할 수 있다.

// read file
{
    const string filename = "randomaccess.txt";
    ifstream IFS(filename);
    IFS.seekg(0, ios::end);// move to end
    cout << IFS.tellg() << endl; // tellg tell current location

    IFS.seekg(5); // seekg(5, ios::beg), move 5bytes and read
    cout << (char)IFS.get() << endl;

    IFS.seekg(5, ios::cur); // move 5bytes from current
    cout << (char)IFS.get() << endl;

    IFS.seekg(-5, ios::end);// move -5bytes from end
    cout << (char)IFS.get() << endl;

    string str;
    getline(IFS, str);
    cout << str << endl; // getline form the current cursor
}

 

파일 읽고 쓰기를 동시에 할 수 있다. fstream을 사용하자.

// read and write
{
    //fstream InOFS(filename, ios::in | ios::out);
    fstream InOFS(filename);
    InOFS.seekg(5);
    cout << (char)InOFS.get() << endl; // read

    InOFS.seekg(5);
    InOFS.put('A'); // write
}

 

https://www.cplusplus.com/reference/istream/istream/seekg/

 

istream::seekg - C++ Reference

public member function <istream> <iostream> (1)istream& seekg (streampos pos); (2)istream& seekg (streamoff off, ios_base::seekdir way); Set position in input sequence Sets the position of the next character to be extracted from the input stream. Internall

www.cplusplus.com

 

Comments