차근차근/C

c++ , map의 key값 중복시 => multimap사용

예쁜꽃이피었으면 2014. 8. 18. 14:26

map을 사용하는데

key값이 중복된다면

multimap을 사용하면 된다.



https://kldp.org/node/93252

안녕하세요...

std::map를 이용해서 자료를 저장하려고 합니다.

std::map사용시에 들어갈 자료가 세개라서
따로 맵을 이중으로 썼습니다.

std::map m1;
std::map, server&> m2;

그런데 문제는 m1의 맵의 키값이 중복이 될 수도 있습니다. 
결국 제가 하고 싶은건 m2의 키값 즉 m1의 값을 가지고 저장시키고 싶은데
다른 방법이 있을까요...


=>답1

키를 중복시키고 싶다면 std::map 대신에 std::multimap을 쓰면 되오.

=>답2

도움이 될지 모르겠지만 http://kldp.org/node/92964 를 참고해 보심이...



=========================================================

http://kldp.org/node/92964

몇일전에 http://kldp.org/node/92100 에서 c++ string stream 및 map, vector 사용법에 대해서 공부했었습니다. 그런데, 또 한가지 정말 이해가 안되는 결과가 발생하고 있습니다. 혹시 xylosper 님이나 doldori 님께서 이 글을 보신다면 한번 살펴 봐주시면 감사하겠습니다.

문제는 뭐냐하면, input file 을 통해서 string stream 을 받고, 받는 즉시 출력을 하면 모든 입력 필드들이 정상적으로 접수(?) 되는 것을 확인 할 수 있었습니다. 하지만, 이 입력 필드들을 map 에 집어넣고 iterator 를 통해서 꺼내보면, 몇몇 입력 line 들이 random 하게 skip 되어 제대로 입력이 안되는 문제가 발생하고 있는데, 왜 그러는지 도통.... 모르겠습니다. (이 문제로 또 이틀째 헤딩중입니다... ㅡ.ㅡ;;;)

일단 input file과 c++ 파일은 아래와 같습니다.
[source] http://nopaste.com/p/aBN3mgeTN
[input] http://nopaste.com/p/alFaxO0aS

콘솔에서

g++ -Wall foo.cc -o foo

와 같이 컴파일을 한 후,

./foo foo.out TFWC

와 같이 실행시킵니다.

foo 가 하는일은 option을 TFWC 로 주면, tfwc_indiv_#.xg 라는 파일을 만들어주는데, 그 파일 내용의 첫번째 컬럼은 +/-/r 등과 같은 기호를 나타내 주고, 두번째 컬럼은 시간 값, 세번째 컬럼은 패킷 사이즈를 나타내 줍니다.

그런데, 웃긴일은

아래 부분코드에서 cout 으로 출력하면 foo.out 에 있는 값들을 그대~~로 가져와서 standard output 으로 출력해주는데 반해서,

            // record map value if packet type matches
            if (!tPacket.compare(target)) {
                values[sId][time] = pStatus;
                cout << pStatus << " " << time << " " << tPacket << " "
                    << pSize << " " << sId << " " << dId << " "
                    << aSeq << " " << bSeq << endl;
                psz = pSize;    // packet size
            }

아래와 같이 iterator 를 사용하여 출력을 해 보면, 어떤 입력 line 들은 아예 map 에 제대로 들어가지 못한 것을 발견할 수 있습니다. (아니면, iterator 가 제대로 map의 값들을 못가져오는건지도....)

    int size = values.size();
 
    for (i=0, pitr = values.begin(); i < size && pitr != values.end(); ++pitr, i++) {
 
        // string stream (to create file names accordingly)
        stringstream ss;
        ss << "trace/" << option << '_' << i+1 << ".tr";
 
        // opne a file and prepare to write
        fout.open(ss.str().c_str());
 
        // record "+/-/d" and then time stamp, and packet size
        for (itr = pitr->second.begin(); itr != pitr->second.end(); ++itr) {
            fout << itr->second << " " << itr->first << " " << psz << endl;
        }

그 증거로....

cat foo.out | awk '{if($1 == "+" && $5 == "TFWC") print}' | wc -l
8929

+ 기호가 있는 것을 원본 입력파일에서 출력하면 총 8929 개의 입력 라인이 있게 되는데 반해,

cat tfwc_indiv_1.tr | awk '{if($1 == "+") print}' | wc -l
8801

즉, 원본 파일에서 "+" 기호가 있는 라인을 그대로 출력하면 8929개 라인인데,
이걸 map 을 이용하여 집어 넣고 iterator 를 사용해서 출력해 보면 8801 라인밖에 안 들어와 있습니다!!! ㅠ.ㅠ

왜 이렇게 어떤 라인들이 skip 되어서 map으로 들어가는지??
아니면 iterator 로 사용해서 map의 값들을 빼낼때 왜 skip 해서 가져오는지??

한수만 더 지도 부탁드리겠습니다.
긴 글 읽어주셔서 더불어 감사드립니다.


=>답1

map 의 특성을 잘 기억해 보십시오.

key 가 겹치는 경우 key 의 값을 덮어 씌우는 특성이 있습니다.

확인해보심이 어떨까요 :)

Neogeo - Future is Now.







==========>

지난번 글을 자세히 읽어보니 xylosper님께서 이미 답을 말씀해주셨더군요.
(URL: http://kldp.org/node/92100)

위의 neogeo 님께서 말씀하신대로 key 값이 중복될 경우, C++ map 이란놈이 smart 하게 처리해주지는 못하는군요...
(key 값이 중복될경우 먼저번 값을 덮어써버리는 경우가 발생하는....)

저의 예제에서는 시간값 (key 값)이 중복되는 경우가 자주 발생하였고, 이런경우 먼저번 값을 나중 값이 덮어써 버리는 경우가 발생했고, 저는 그것이 map 이 오류 아니면 iterator의 오류로 착각했었습니다.

이 문제를 해결하는데 핵심은, xylosper 님께서 말씀해주신 대로 multimap을 이용하는 것이었습니다.

따라서, 제 코드중에 바뀌어야 할 부분은 이러했습니다.

일단 map declaration 부분을 아래와 같이 수정하고...

    // map declaration
    typedef multimap <double, string> t_mmap;
    typedef map <double, t_mmap> t_map;
    t_map imap;
 
    // iterator declaration
    multimap <double, string>::iterator itr;
    map <double, t_mmap>::iterator pitr;

그리고, 값을 적어주는 부분을 아래와 같이 수정했습니다.

    if(fin.is_open()) {
        while (getline(fin, sline)) {
            istringstream isOk(sline);
            isOk >&gt; pStatus;    // 1: packet received status
            isOk >&gt; time;       // 2: time stamp
            isOk >&gt; nil;        // 3: do not need this column
            isOk >&gt; nil;        // 4: do not need this column
            isOk >&gt; tPacket;    // 5: packet type
            isOk >&gt; pSize;      // 6: packet size
            isOk >&gt; nil;        // 7: do not need this column
            isOk >&gt; nil;        // 8: do not need this column
            isOk >&gt; sId;        // 9: source Id
            isOk >&gt; dId;        // 10: destination Id
            isOk >&gt; aSeq;       // 11: sequence number
            isOk >&gt; bSeq;       // 12: sequence number
 
            // record map value if packet type matches
            if (!tPacket.compare(target)) {
                imap[sId].insert(t_mmap::value_type(time, pStatus));
                psz = pSize;    // this is actual packet size
            }
        }
        fin.close();    // close file
    } else {
        cout &lt;< "error opening file!" &lt;< endl;
        exit (1);
    }

이렇게 했더니만, 제가 본 글을 띄울때 발생했던 부분이 말끔히 해결이 되었습니다.

그래서, 완성된 full version 은
http://nopaste.com/p/asg6kSCbmb 가 되었습니다.

긴~~글 읽어봐 주셔서 감사드리구요, 다른 커멘트 있으면 역시 환영입니다.
(이렇게 여러분들의 커멘트 받으면서 공부하니까 많이 도움이 되는 것 같습니다.)

감사합니다!


반응형