[완료] C++ std::map, std::iterator 관련 질문입니다.

s.choi의 이미지

몇일전에 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 해서 가져오는지??

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

neogeo의 이미지

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

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

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

Neogeo - Future is Now.

Neogeo - Future is Now.

s.choi의 이미지


아하! 그렇군요!! key 값이 (제 예제에선 시간값)이 중복되는 경우가 많습니다.
이 문제때문에 저런 현상이 발생할 수 있는 건가요?

도움말씀 감사드립니다~

s.choi의 이미지

key 값에 따라 map이 불필요하게 (?) overwrite을 하게 되는 속성(?)이 있다면...

map 선언할때의 아래부분과

    // map declaration
    map <double, map <double, string> > values;
 
    // iterator declaration
    map <double, map <double, string> >::iterator pitr;
    map <double, string>::iterator itr;

그리고, map 에 값을 기록하는 부분의 수정이 필요한건가요??

            if (!tPacket.compare(target)) {
                values[sId][time] = pStatus;
                psz = pSize;    // packet size
            }

어떤 방법이 있는지 혹시 힌트 주시면 감사하겠습니다~

s.choi의 이미지

지난번 글을 자세히 읽어보니 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 >> pStatus;    // 1: packet received status
            isOk >> time;       // 2: time stamp
            isOk >> nil;        // 3: do not need this column
            isOk >> nil;        // 4: do not need this column
            isOk >> tPacket;    // 5: packet type
            isOk >> pSize;      // 6: packet size
            isOk >> nil;        // 7: do not need this column
            isOk >> nil;        // 8: do not need this column
            isOk >> sId;        // 9: source Id
            isOk >> dId;        // 10: destination Id
            isOk >> aSeq;       // 11: sequence number
            isOk >> 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 << "error opening file!" << endl;
        exit (1);
    }

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

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

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

감사합니다!

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.