stringstream

Computer/C/C++ 2007. 8. 9. 22:17
재미있는 녀석을 발견했다.


stringstream 이라는 녀석...

출처: http://blog.empas.com/electr/19358611

1.요약

문자열을 스트림처럼 다룰 수 있습니다.
sprintf/wsprintf의 대용으로 사용할 수도 있고, 문자열에서 토큰을 꺼내오는 데도 사용할 수 있습니다.


2.본문

C++에서 표준 입출력을 나타내는 cin/cout을 사용해 보신 적이 있으실 겁니다. stringstream도 같은 인터페이스를 가지고 있습니다.
대신에 문자열을 상대로 입출력을 한다고 생각하시면 되죠.

예제를 우선 보시겠습니다.

----- stringstream을 사용한 예제 --------------------------

1 

2 

3    #include <sstream> 

4    #include <iostream> 

5     

6    int main(int argc, char* argv[]) 

7    { 

8        int n = 3571; 

9        char* p = "blue"; 

10     

11        std::stringstream s; 

12        s << " My favorite color is " << p << "nand my favorite number is " << n; 

13     

14        std::cout << "Sentence----------------------n"; 

15        std::cout << s.str() << std::endl; 

16     

17        std::cout << "nToken-------------------------n"; 

18     

19        while(1) 

20        { 

21            char c[100]; 

22     

23            s >> c; 

24            if( s.fail() ) 

25                break; 

26     

27            std::cout << c << "n"; 

28        } 

29     

30        std::cout.flush(); 

31     

32        return 0; 

33    } 

---- 실행 결과 -------------------------------------------------

Sentence---------------------
My favorite color is blue
and my favorite number is 357

Token------------------------
My
favorite
color
is
blue
and
my
favorite
number
is
3571

-- 예제 분석 -----------------------------------------------

3    #include <sstream> 
stringstream 은 sstream 헤더 파일에 정의되어 있습니다.

( 참고로 이번 예제에서는 using namespace std;를 사용하지 않았기 때문에 필요할 때마다 std::를 붙여주어야 합니다. 계속 보시죠)

----------------------------------------------------------

8        int n = 3571; 

9        char* p = "blue"; 

10     

지역 변수를 선언하고 있습니다.
n, p는 나중에 문자열에 집어넣으려고 만든 겁니다.

중요한 stringstream s를 선언하고 있습니다.

----------------------------------------------------------

12        s << " My favorite color is " << p << "nand my favorite number is " << n; 
이번 Tip에서의 첫번째 하이라이트 입니다. MFC의 CArchive를 쓰듯이 s에 문자열 및 숫자를 차곡차곡 넣고 있습니다.

----------------------------------------------------------

15        std::cout << s.str() << std::endl; 
넣었으면 전체 문자열을 얻을 수도 있어야 겠지요.
stringstream::str() 은 string 을 반환합니다.

string은 C++ Standard Library에서 문자열을 다루기 위해 추가된 템플릿 클래스 입니다. string 객체에서 c-style의 문자열을 얻고자 하신다면.

str.c_str(); 
처럼 하시면 됩니다. 필요하실 때 쓰세요.

----------------------------------------------------------

19        while(1) 

20        { 

21            char c[100]; 

22     

23            s >> c; 

24            if( s.fail() ) 

25                break; 

26     

27            std::cout << c << "n"; 

28        } 

문자열의 마지막까지 토큰을 뽑아내려고 while 문으로 루프를 돌리고 있습니다.

이번 Tip의 두 번째 하이라이트인 23 라인에서는

s >> c;
처럼 토큰을 뽑아내고 있습니다.

나머지 코드는 실패여부를 확인하고 화면에 찍어주는 역할을 하므로 설명은 생략하겠습니다.


3.예제



4.참고

The C++ Standard Library from scratch



- 2001.08.13 Smile Seo -

'Computer > C/C++' 카테고리의 다른 글

gdb 명령어 요약집  (0) 2007.09.20
#define을 통한 macro에서 #과 ##의 용법  (0) 2007.08.08
#pragma  (0) 2007.08.08
vprintf, vsprintf,... 가변 인수 함수.  (0) 2007.08.01
bitset 클래스  (0) 2007.07.02

bitset 클래스

Computer/C/C++ 2007. 7. 2. 14:26

뭔가 bitmap을 만들어서 사용할 일이 발생했다.

shift와 modulo 연산을 열라게 하게 될 줄 알고 내심 고민..어떻게 효율적으로 짤 수 있을까 잠시 더 고민...

하다가 STL에 놀라운 녀석이 있다는 사실을 발견하였다.

------------------------------

비트 단위의 조작을 할때 사용할 수 있는 클래스.

가령, 10 비트 공간안에 10개의 값을 저장할 때 사용.

그러나, bitset 클래스가 메모리를 비트 단위로 사용하는 것은 아니고 외부로 표현 될 때 10개의 비트처럼 보인다는 거라넹~~~~


#include <iostream>
#include <bitset>


using namespace std;


int main()
{
    bitset < 5 > flags;


    flags.set ( 0 );
    flags.set ( 1 );
    flags.set ( 2 );


    cout << "상태 : " << flags << endl;

    cout << "any  : " << flags.any( ) << endl;

    cout << "test : " << flags.test(3) << endl;


    flags.reset( 1 );
    cout << "상태 : " << flags << endl;
    cout << "개수 : " << (int)flags.count() << endl;

    return 0;
}


[결과]

상태 : 00111

any  : 1

test  : 0

상태 : 00101

개수 : 2



멤버 함수로는

bitset::any()  - 어떤거라도 켜져 있으면 true 를 return.

bitset::test()  - 몇 번째 비트의 값이 켜져 있는지 꺼져 있는 지 return.

bitset::set()   - 몇 번째 비트의 값을 설정하는 함수.  ( default : 1 )

                      모든 비트를 1로 설정하는 매개 변수 없는 버전 과 특정 비트만 설정하는 매개변수

                      있는 버전이 있다. 매개 변수 있는 버전에서는 두번째가 지정할 값인데 default로

                      1 이기 때문에 인덱스만 지정해서 호출하면 1로 설정이 된다.

                 단, 일반적으로 set()은 설정, reset()은 해제라고 생각하기 때문에 만약 설정값을

                      0 (false) 으로 두면 착각할 수 있다라고 저자는 얘기하고 나 또한 그럴것 같다.

bitset::reset() - 비트를 0으로 설정하는 멤버 함수.

bitset::count() - 켜진 비트 개수를 return. size_t type로 return.


- <프로젝트와 함께 하는 STL의 아름다움> 491page 참조



출처 : http://blog.naver.com/rudalson/100011108867

'Computer > C/C++' 카테고리의 다른 글

#pragma  (0) 2007.08.08
vprintf, vsprintf,... 가변 인수 함수.  (0) 2007.08.01
STL - MAP  (0) 2007.02.27
컨테이너 초기화및 Functor 예제  (0) 2007.02.27
STL lower_bound function 사용하기 Sample...  (0) 2007.02.27

STL - MAP

Computer/C/C++ 2007. 2. 27. 20:32

  STL 맵들은 기본 컨테이너들 중에서 아마 가장 복잡하고도(상대적으로 말해서) 가장 다목적인 컨테이너들일 것이다. 이 글에서는 가장 기본적인 맵인 map에 대해서만 이야기하고, 다른 트리 기반 구조체(set, multise, mulitimap) 들에 대해서는 생략하겠다. 한 종류의 맵에 대해서는 확실히 알게 되면 다른 것들도 쉽게 이해할 수 있다.


  map은 본질적으로 키-값 쌍들을 담는 컨테이너이다. map에는 임의의 두 종류의 데이터들이 키/값 상의 형태로 저장된다. 키를 통해서 값을 조회하는 데 걸리는 시간은 O(log n)인데, 해시 테이블보다는 비효율적이지만 속도의 차이는 무시할 수 있는 정도이며 삽입과 함께 정렬이 수행된다는 추가적인 장점이 존재한다. 다시 말해서는 map의 항상 정렬된 상태로 존재하는 것이다. 이는 map의 저장 방식 자체(균형 이진 트리(balanced binary, tree), 또는 적흑 트리(red-black tree)라고도 한다)가 가지고 있는 장점이다.


#pragma warning(disable:4786)
#include <map>
#include <iostream>
#include <string>
#include <algorithm>

 

using namespace std;

 

//. 맵 컨테이너들을 비교하는데 쓰이는 템플릿
template <class F, class S>
class value_equals
{
private:
   S second;
public:
   value_equals(const S& s) : second(s) {}
  

   bool operator() (pair <const F, S> elem)
   {
    return elem.second == second;
    }
};

 

//. 코드의 입력과 가독성을 돕기 위한 typedef들
typedef map<int, string> isMap;
typedef isMap::value_type isValType;
typedef isMap::iterator isMapItor;

 

void main()
{
 

isMap c;

 

 //. 키-값 쌍들을 삽입
 c.insert(isValType(100, "One Hundered"));
 c.insert(isValType(3, "Three"));
 c.insert(isValType(150, "One Hundred Fifty"));
 c.insert(isValType(99, "Ninety Nine"));

 

 //. 모든 키들과 값들을 출력
 for(isMapItor itor = c.begin(); itor != c.end(); ++itor)
   cout << "Key = " << (*itor).first << ", Value = " << (*itor).second << endl;


 //. 맵을 연관 배열처럼 다룰 수 있다.
 cout << "Key 3 Displays value " << c[3].c_str() << endl;

 

 //. 다음과 같은 방식으로 키 - 값 쌍을 삽입하는 것도 가능하다.
 isMapItor pos = c.find(123);

 if(pos != c.end())
 {

  //. 요소를 삭제하면 그것을 가리키는 반복자는 무효화 된다.
  //. 그 상태에서 그냥 pos++ 를 호출하면 정의되지 않은
  //. 행동이 발생할 것이다.

  c.erase(pos);
 }


 //. 값에 기반해서 특정한 요소를 찾고 제거한다.
 pos = find_if(c.begin(), c.end(), value_equals<int, string>("Ninety Nine"));
 

if(pos != c.end())
   c.erase(pos);

 

 //. 루프 도중에 요소를 제거하는 경우에는 이런식으로..
 for(isMapItor itr = c.begin(); itr != c.end();)
 {
   if(itr->second == "Three")
     c.erase(itr++);
   else
     ++itr;
  }

}


  value_type이라는 새로운 중간 데이터형은 컨테이너 안에 키 - 값 쌍의 형태로 저장되는 요소들은 나타내기 위한 것이다. 편의를 위해서, typedef를 이용해서 이 데이터형과 다른 데이터형들을 좀더 쓰기 쉬운 형태로 정의했다.

 

  요소(키-값 쌍)를 컨테이너에 삽입할 때에는 insert() 함수를 이용한다. 다른 컨테이너들과 유일한 차이는 map::value_type 형의 값을 넣는다는 점이다. map은 요소가 삽입될 때 자동적으로 그것을 정렬하므로 컨테이터는 항상 키에 의해 정렬된 상태를 유지한다. map을 루프로 돌려서 키들과 그에 연관된 값들을 출력하므로 이 점을 확일 할 수 있다.

 

  map은 두 개의 항목(키와 데이터)을 쌍으로 저장하는 컨테이너이므로 하나의 반복자로 두 개의 항목들에 각각 접근하는 것이 가능하려면 또다른 구조체가 존재해야 한다. 그것이 바로 value_type 구조체이다. 반복자를 역참조하면 value_type 구조체를 얻게 된다. value_type에는 first와 second라는 두 개의 데이터 멤버들이 있는데, first 키 값을 담고 있으며 second는 데이터 값을 담고 있다.

 

  map은 키 값을 통한 임의 접근도 허용한다. 이 경우 map은 연관 배열(또는 희소 배열)과 동일한 방식으로 작동하게 된다. 요소들에 접근하거나 요소들을 추가할 때에는 index() 연산사를 사용한다. 이 연산자를 사용할 때에는 주의할 점이 있다. 이 연산자로 아직 존재하지 않은 요소에 접근하려 하면 에러가 발생하는 대신 기본 생성자에 의해 새 요소가 생성되고 그것이 map에 삽입된다. 에러를 발생시키지 않고 새 요소를 추가한다는 것은 논리적인 버그의 원인 될 수 있으므로 주의하기 바란다.

 

  코드 후반부에는 find() 함수로 특정 키에 해당하는 요소를 찾는 부분이 나온다. 키들을 정렬되어 있으므로, find()의 수행 시간은 O(log n) 이다.

 

  값에 기반해서 요소를 찾는 것은 조금 더 복잡하다. 요소들은 키에 기반해서 정렬되어 있으므로 값에 기반해서 요소를 찾는데 필요한 시간은 O(n)가 된다. 이번 예제에서는 루프 구조 대신 find()_if()라는 범용적인 STL 알고리즘을 이용해서 검색을 수행한다. 이 알고리즘에는 세 개의 인자들을 통해서 검색의 시작 위치를 뜻하는 반복자와 종료 위치를 뜻하는 반복자, 그리고 알고리즘이 검색 성공 여부를 판단하는 데 사용할 함수 객체를 지정해야 한다. 두 반복자는 자명하므로 설명할 필요가 없겠지만 함수 객체(functior)에 대해서는 족므 설명이 필요할 것 같다.

 

  STL에서는 함수들 대신 중복된 함수 연산자들을 가진 클래스들이 쓰인다.(함수 연산자를 중복하는 것이 가능하다는 것을 처음 알게 된 독자도 있을 것이다) 이러한 방식은 일반적인 프로그래밍 문제에 캡슐화된, 그리고 형에 안전한 해결책의 제공을 가능하게 한다. 이번 예제에서 지정한 함수 객체는 단순 value_pair의 second 값을 비교하고 그 결과를 돌려 준다. STL에는 대부분의 문제들에서 코드에 즉시 써먹을 수 있는 함수 객체들이 준비되어 있다. 여러 알고리즘들과 그에 사용할 수 있는 함수 객체들에 대해서는 STL 관련 서적을 참고하길 바란다.

 

  find_if() 기법은 컨테이너에서 어떠한 값을 찾을 때 권장되는 방식이나, map을 직접 루프로 돌려서 요소들을 제거해야 하는 경우도 생길 수 이 있다. 코드 마지막 for 루프가 그에 대한 것이다. 그런데 STL 설계자들은 다른 컨테이너들에서와 달리 map에 대해서는 erase() 함수가 다음의 유효한 위치를 돌려주도록 구현하지 않았다.(아마 속도상의 문제 때문일 것이다.) 이 때문에 다른 컨테이너들에서와는 조금 다른 방식으로 요소들을 제거해야 한다. 요소의 제거에 따른 반복자의 무효화 피하기 위해서는 약간의 우회책이 필요하다.

 

  이번 예제에서는 for 루프문 안에서 반복자를 증가시키는 대신 루프의 본문 안에서 조건에 따라 바복자를 증가시킨다. 요소를 제거해야 하는 경우에는 후행 증가(반복자를 참조한 후 증가시킨다.)를 사용했고, 요소를 제거하지 않는 경우에는 선행 증가(먼저 증가시킨 후 참조)를 사용했다. 이런 방식을 이용하면 임시적인 반복자에 유효한 위치를 보존하는 등의 번거로움을 피할 수 있다. 그러나 선행 증가와 후행 증가의 차이에 의존하는 것은 코드를 이해하기 힘들게 만들며 디버깅이나 유지보수에도 좋지 않다. STL 설계자들이 map의 erase()를 다른 컨테이너들의 erase()와 동일한 방식으로 만들더라면(속도를 희생하더라도) 도 좋지 않았을까 싶다. 표준 위원회가 이러한 문제를 이해해서 다음 버전의 STL에서는 보다 낭느 방향으로 개선이 이루어지길 바란다.

 

  여기에서는 교과서적인 이야기를 하나 하고 넘어가야 할 것 같다. 루프 안에서는 항상 선행 증가를 사용하는 것ㅇ이 좋은 이유는 무엇일까?

 

  효율성 때문이라는 것이 정답이다. 후행증가 연산자는 변수의 이전 값을 돌려주므로 이전 값을 담을 임시적인 변수를 만들고 파괴하는 과정이 일어나게 된다. 후행 증가로도 선행증가와 동일한 방식의 루프를 만드는 것이 가능하지만, 후행 증가를 사용할 특별한 이유(위의 예제 나온 것 등)가 없다면 항상 선행 증가 또는 선행 감소 연산자를 사용하는 것이 바람직하다.

 

- 출저 : Game Programming Gems 1  중에서-

출처 : Tong - duragon님의 VC++통

'Computer > C/C++' 카테고리의 다른 글

#pragma  (0) 2007.08.08
vprintf, vsprintf,... 가변 인수 함수.  (0) 2007.08.01
bitset 클래스  (0) 2007.07.02
컨테이너 초기화및 Functor 예제  (0) 2007.02.27
STL lower_bound function 사용하기 Sample...  (0) 2007.02.27

컨테이너 초기화및 Functor 예제

Computer/C/C++ 2007. 2. 27. 19:57

Retrieved From : http://blog.naver.com/ziralist?Redirect=Log&logNo=7708214
====================================================
가령 int형 배열을 생성시켜서 이를 난수로 초기화하려면 아래

예제처럼 하면될 겁니다만...(참고로 실행환경은 VC60입니다)

 

#include <iostream>
#include <algorithm>
#include <stdlib.h>

 

int main( )
{
 int nums[5];
 std::generate_n(nums, 5, rand); //여기서 난수로 초기화
 
 for(int i = 0; i < 5; i++)
  std::cout << nums[i] << ' ';
 
 system("pause");
 return 0;
}

 

이렇게 STL algorithm 헤드파일에 선언된 generate 계열함수를 사용하면

되지만 문제는 세번째 인수인 함수포인터가 반드시 int (*)(void)형의 데이

터 타입을 가져야하기 때문에 random() 같은 함수를 사용하여

 

일정 범위내의 난수로 초기화하는데 어려움을 겪게 됩니다. 하지만 이 경우

함수객체 Functor를 사용하면 어느정도 융통성을 발휘할 수가 있게 됩니다

일단 rand() 함수를 객체화하려면 함수호출 연산자 ()를 오버로딩하여

 

class CRand
{
public:
 int operator() (void) { return rand();}
};

 

이렇게 하면 됩니다 이것은 정확한 int rand(void)함수의 객체입니다

여기서 int random(int)와 같은 기능을 하는 객체로 진화시켜 봅니다

 

class CRandom
{
public:
 CRandom(int limit = RAND_MAX) : _limit(limit) {}
 int operator() (void) { return rand() % _limit;}
private:
 int _limit;
};

 

이렇게하면 적어도 기능상으로는 random() 함수와 완전히 같아지게 됩니다

또한 동시에 generate 계열 함수에도 성공적으로 적용됩니다 왜냐하면 STL

의 generate() 계열 함수는 템플릿으로 설계되어 있어

 

그 세번째 인수로 함수명을 넘기면 함수 포인터를, 클래스를 넘기면 클래스

를 파라미터로 받는 generate()가 생성되기 때문입니다. 다만 generate()

내부적으로 호출하는 함수의 prototype으로

 

(*)(void) 데이터형을 요청하기 때문에 generate()에 투입되는 함수객체의

operator() 멤버함수는 반드시 (*)(void) 데이터타입을 지켜줘야 한다는 제

약이 따르게 됩니다 하지만

 

이러한 제약이 따르는 상황에서도 함수객체, 즉 Functor는 생성자 함수를

사용하여 지맘대로 파라미터를 설정하여 인수로 넘길 수 있게되어, 다채

운 함수작동 환경을 보다 델리케이트하게 조율할 수 있습니다


일케 알기쉽게 함수포인터의 갑갑함을 벗어나 보다 유연하게 코딩하며 STL의

이른바 "일반화 알고리즘"의 혜택을 만끽할 수있다는 것이 함수객체의 자랑입

니다 또한 함수객체의 멤버함수 operator()를 인라인으로 처리하면

 

generate()와 같이 내부적으로 루프 속에서 Functor::operator()를 반복적

으로 수행하는 함수의 경우 상당한 속도개선 과를 기대할 수 있다는 잇점도

따르게 됩니다 아래에 완성된 예제를 싣습니다


 

#include <iostream>
#include <algorithm>
#include <stdlib.h>


class CRandom
{
public:
 CRandom(int limit = RAND_MAX) : _limit(limit) {}
 int operator() (void) { return rand() % _limit;}
private:
 int _limit;
};

 

int main( )
{
 int nums[5];
 std::generate_n(nums, 5, CRandom(100));
 
 for(int i = 0; i < 5; i++)
  std::cout << nums[i] << ' ';
 
 system("pause");
 return 0;
}

'Computer > C/C++' 카테고리의 다른 글

#pragma  (0) 2007.08.08
vprintf, vsprintf,... 가변 인수 함수.  (0) 2007.08.01
bitset 클래스  (0) 2007.07.02
STL - MAP  (0) 2007.02.27
STL lower_bound function 사용하기 Sample...  (0) 2007.02.27

STL lower_bound function 사용하기 Sample...

Computer/C/C++ 2007. 2. 27. 19:42
#include <iostream>
#include <string>
#include <set>
#include <algorithm>
#include <vector>
#include <ostream>
#include <list>
#include <iterator>
using namespace std;

class DataTuple {
private:
    int sourceID;
    int value;
    int high;
public:
    DataTuple();
    DataTuple(int sourceID, int value) {
        this->sourceID = sourceID;
        this->value = value;
        this->high = value+10;
    }
    bool operator<(const int &a) const
    {
        return (value<=a);
    }
   
   
    // Getters and Setters
    int getSourceID() {return sourceID;}
    void setSourceID(int sourceID) {this->sourceID = sourceID;}
    int getValue() {return value;}
    void setValue(float value) {this->value = value;}
};
DataTuple::DataTuple(){
    ;
}

class Something{
    public:
        bool operator()(DataTuple a, DataTuple value)  {
            return ( a.getValue() <= value.getValue() );
        }
};

int main()
// illustrate the use of the generic set algorithms
{
     list<DataTuple> a;
     list<DataTuple>::iterator ait, bit;
    
     DataTuple c1 = DataTuple(1,1);
     DataTuple c2 = DataTuple(2,11);
     DataTuple c3 = DataTuple(3,21);
     DataTuple c4 = DataTuple(4,31);
     a.push_back(c1);
     a.push_back(c2);
     a.push_back(c3);
     a.push_back(c4);

     int aaa = 31;

     DataTuple sample1;
     sample1.setValue(30);
     bit = --lower_bound(a.begin(),a.end(),sample1,Something());
    
     cout<<bit->getSourceID()<<endl;
     return 1;
}

compare operator에 const같은 것을 안쓰긴 했지만 아무튼...
이런식으로 사용된다.

'Computer > C/C++' 카테고리의 다른 글

#pragma  (0) 2007.08.08
vprintf, vsprintf,... 가변 인수 함수.  (0) 2007.08.01
bitset 클래스  (0) 2007.07.02
STL - MAP  (0) 2007.02.27
컨테이너 초기화및 Functor 예제  (0) 2007.02.27