Ch17 stream

19 분 소요

표준입출력(standard input output)

CLI 에서 보통 쓰는게 표준 입출력. stdio 인 이유가 standard input output 임.

알고리즘 문제 풀 때 가끔 표준출력으로 제출하시오 이렇게 나옴.

C/C++ 은 표준입출력이 언어에 내장 안됨

많은 언어들이 내부에 입출력 기능을 언어 자체에 내장하고 있으나 c, c++ 은 없음.

그래서 우리는 <stdio.h> 같은걸 Include 하는 것.

이는 컴파일러 설계자가 입출력 함수를 하드웨어에 가장 적합하게 설계할 수 있게하기 위해서임.

C 에서

우리가 자주 쓰는 <stdio.h> 같이 Unix 환경에서 개발된 몇몇라이브러리는 __ANSI C__에서 표준 입출력(standard input output) 패키지로 구성해놔서 간단히 가져다 쓸 수 있음.

C++에서

<iostream> 이 ANSI/ISO C++ 에서 STL(Standard Template Libray) 이랑 같이 C++ Standard Libary(std namespace 에 있음) 로 들어감.

Stream

flowchart LR A(Device/Program) <--> B(Stream) <--> C(My C++ Program) classDef common fill:#000,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5; class A,B,C common;

어떤 장치/프로그램에서 입출력을 해도 C++ 이 같은 방법으로 처리할 수 있게 하는 매개체

이때 보통 임시 저장용 버퍼를 만들어서 장치 간 정보전송률 차이를 줄임

  • 예를들어 CPU 는 명령어당 처리할 수 있는 단위가 작지만 데이터 이동속도가 빠름
  • 근데 HDD 는 느리니까 한번에 여러바이트를 RAM 에 보냄

스트림 = 버퍼 + 추가기능 대충 이렇게 보면 됨

C++ 의 스트림

stl 에서 stream 은 cout, cin, ofstream, ifstream sstream 등이 있고 아래에서 살짝 다룰 것임.

지금 주목할 것은 이런 stream 은 모두 ios_base 를 부모로 둔 <iostream> 에 있는 쓰는 ostream(out), istream(in) 중 하나를 상속 받는 다는 것

  1. C++ 에서 stl 에 cout, iostream 같은 stream 을 만들어 줬고

  2. 애네들은 ostream, istream 를 상속 받으며

  3. 얘내로 만든 stream 객체가 안에 소숫점 아래 자리수, 10진법이냐 16진법이냐, 입력이 제대로 되었냐 같은 정보를 담아서 입출력 데이터를 제어하게 됨.(사용해봤을거임) => 기능

  4. 그리고 그 안에서 streambuf 객체를 사용함. => 버퍼

즉 앞에서 말한 Buffer 와 추가기능 이 C++ STL 의 Stream 에서 이렇게 적용이 되는 거임.

참고

__PURE_APPDOMAIN_GLOBAL extern _CRTDATA2_IMPORT istream cin, *_Ptr_cin;
__PURE_APPDOMAIN_GLOBAL extern _CRTDATA2_IMPORT ostream cout, *_Ptr_cout;
__PURE_APPDOMAIN_GLOBAL extern _CRTDATA2_IMPORT ostream cerr, *_Ptr_cerr;
__PURE_APPDOMAIN_GLOBAL extern _CRTDATA2_IMPORT ostream clog, *_Ptr_clog;

<iostream> 에서 표준입출력 장치 와 연결을 보여주는 부분. (뭔 장치인지는 변수명곧내)

cin, cout 이런 애들이 ios_base(왜냐면 i,o,stream 이니까) 을 왜 상속받는지 보여줌

참고로 wchar_t 버전도 그 밑에줄에 있음

template <class _Elem, class _Traits>
class basic_ostream : virtual public basic_ios<_Elem, _Traits> { // control insertions into a stream buffer
public:
    using _Myios = basic_ios<_Elem, _Traits>;
    using _Mysb  = basic_streambuf<_Elem, _Traits>;
    using _Iter  = ostreambuf_iterator<_Elem, _Traits>;
    using _Nput  = num_put<_Elem, _Iter>;

밑에서 4번째 줄을 보면 알 수 있 듯 이러한 스트림은 streambuf 객체를 이용함.

istream 도 찾아보면 비슷하게 나옴.

inline char ReadChar() {
	static char readbuffer[200024];
	static int buf_p, buf_l;

	if (buf_p == buf_l) {
		buf_l = (int)fread(readbuffer, 1, 200024, stdin);
		buf_p = 0;
	}
	return readbuffer[buf_p++];
}

참고. stdio 를 이용해서 buffer 와 표준입출력(저기선 stdin) 을 직접 연결하면 위와같은 코드가 됨.(PS 풀 때 대용량 인풋에서 종종 괜찮음)

Redirection

표준입력과 표준출력이 대부분의 OS 에서 키보드와 화면임

이러한 표준입출력을 다른쪽으로 옮겨주는게 리디렉션

// String2Count.cpp
int main()
{
	cout << "길이는 ";
	string input;
	cin >> input;
	cout << input.size() << endl;
}
cmd<Test.txt
길이는 7
cmd>String2Count <Test.txt >Ans.txt

Window CMD 에선 <> 으로 Redirection 을 함.

Redirection 명령인 <Test.txt 으로 Test.txt 의 텍스트(위에선 “abcdefg” 넣음) 를 String2Cout.cpp 의 cin 으로 넣게 됨. 그럼 cout 에 버퍼엔 “길이는 7” 이 들어갈 거임.

표준출력을 Redirection 을 안하면 cout 이 그대로 CLI(커맨드 그거) 에 출력함

>Ans.txt 로 Redirection 을 하면 cout 이 Ans.txt 로 가서 “길이는 7” 이라고 적음

이걸 내가 쓸 일이 있을까? 쓰는 예

cout, cin (표준입출력)

연산자 Overloading

basic_ostream& __CLR_OR_THIS_CALL operator<<(unsigned long long _Val);
basic_istream& __CLR_OR_THIS_CALL operator>>(int& _Val);

template <class _Traits>
basic_ostream<char, _Traits>& operator<<(basic_ostream<char, _Traits>& _Ostr,
    const char* _Val);

기본적인 자료형은 위처럼 ostream, istream 내부에서 Overloading 되어 있음.

type 이 char* 같은거면 끝에 Null 문자 넣어야 정상작동하고 입력인 경우 알아서 결과값에 넣어줌.

struct AAA { 
	int a = 10;
	friend ostream& operator<<(ostream& stream, const AAA& in) { return stream << in.a; }
	friend istream& operator>>(istream& stream, AAA& in) { return stream >> in.a; }
};

int main() {
	AAA a; 
	cin >> a; cout << a;
}

이건 우리가 커스터마이즈 할 때 하는거.

함수의 리턴값 덕분에 cout << "1" << "b" 이렇게 연달아서 가능하단건 다들 알거임

cout (표준 출력)

cout 은 코테용으로도 알아두면 좋은게 있음.

cout 의 버퍼는 일반적으로 512 byte 나 그 배수임.

그리고 std::endl출력과 동시에 버퍼를 비움(flush).

그래서 버퍼가 꽉차기를 기다리지 않고 바로 출력을 원하면 std::endl 이 좋은데 코테에서 속도를 중시해서 cout << asdf << '\n' 이런식으로 많이 씀 아님 printf 쓰던가 힘.

    static constexpr _Fmtflags boolalpha   = static_cast<_Fmtflags>(0x4000);

Ios_base::boolapha 같이 cout.setf() 로 출력형식 설정할 때 쓰는 위와 같은 애들은 비트마스크로 저장되어서 각 비트가 켜져있으면 그에 맞는 처리를 해줌

근데 저런거 하나하나 다 못외우므로 __Manipulators(조정자) __ 라고 매크로도 만들어놓음. 예를들어 cout << cout.setf(ios::boolalpha); => cout << boolalpha; 이렇게. 특이점은 얘는 << 이거로 처리함.

cin (표준 입력)

  • cin 은 >> 이 연산자를 쓰면 whitespace(‘ ‘, ‘\n’, ‘\t’) 을 다 무시함.
  • ‘\n’ 나올 때 까지 버퍼에 값을 넣으며 대기하고 ‘\n’ 이 나오면 다음 명령을 실행함.
  • clear() 는 버퍼를 비우는게 아니고 eofbit, badbit, failbit 같이 입력이 오류없이 제대로 들어왔는지 파악하는 상태( 스트림 비트상태 )를 초기화 (잘못된 입력으로부터 복구하는 용)
  • 이러한 스트림 비트상태가 하나라도 on 이면 stream 이 닫히고 이후의 cin 을 호출해도 아무일도 안함.
  • 스트림 비트상태를 on 시킨 데이터는 그대로 버퍼에 남아있음.
  • 버퍼 비우려면 어디 빈곳에 while 문 돌리면서 출력함.
  • 잘못된 입력에 대해 try-catch 문이 가능하도록 exception 을 발생하도록 설정할 수 있음
  • get, getchar 이런건 지금봐도 헷갈리므로 필요할 때 검색

fstream

  • 어차피 필요할 때 검색하면서 사용할거라 대부분 생략
  • close() 해도 파일과의 연결만 끊어지고 버퍼는 객체가 소멸될 때 해체됨에 주의
  • 얘도 마찬가지로 clear() 가 버퍼 초기화가 아니라 스트림 상태 초기화임. (사실 버퍼 cursor 만 조정하면 되지 초기화할 이유가 없다.)
  • 스트림상태는 is_open, ![객체] 등으로 가능.
  • 한 객체로 동시에 여러개의 파일도 열 수 있음(최대 갯수는 운영체제 나름)
char tmpName[L_tmpnam]; // L_tmpnam 은 define 된 값
tmpnam(tmpName);  // tmpnam 으로 임시파일명 생성

위는 컴파일러가 자동으로 생성하는 임시파일명 생성( sjgw.0 이런식으로 나옴.)

sstream

  • string stream 을 줄여서 sstream
  • 실제로 정의하는 클래스는 istringstream, ostringstream 으로 줄여부르지 않음(?!)
  • cout 이 표준출력, ofstream 이 파일출력이었다면 이건 string 이 출력대상
  • cout 에 있는 웬맨한 기능은 다 적용이 됨.
  • 정수를 hex 로 바꿔서 string 에 넣는다던지 하는 필요가 있을 때 사용하면 좋음
  • [객체].str() 하면 버퍼가 닫히고 완성된 string 이 리턴됨

댓글남기기