[완료] C++로 데이터 처리관련 질문입니다.

s.choi의 이미지

C++ 로 데이터 처리하는 프로그램을 만드려고 하는데요,
간단한 것 같으면서도 잘 안되는 건 왜일까요.. ㅠ.ㅠ

도와주시면 너무 감사드리겠습니다.

아래와 같은 데이터가 있다고 가정해보구요
첫번째 칼럼은 시간,
두번째 칼럼은 특정 값,
세번째 칼럼은 포인터 값을 각각 출력한건데요.

아래 보면, 여러개 포인터 값이 시간순서대로 정렬이 되어 있는데,
이걸 각 포인터별로 시간순서대로 정렬하고픕니다. ㅠ.ㅠ

어떤 방법이 있을까요? 도와주세요~~~~~
아래에는 4개의 서로다른 포인터가 있지만,
최종적으로 토탈 포인터 갯수는 가변적이에요.

(최종 데이터 파일은 첨부 봐주시구요.)

20.000243       357.0           0x86624b8
20.002351       103.0           0x86399f0
20.003571       357.5           0x86624b8
20.005679       103.3           0x86399f0
20.007343       103.7           0x86399f0
20.009007       104.0           0x86399f0
20.010227       358.0           0x86624b8
20.012335       104.3           0x86399f0
20.013999       104.7           0x86399f0
20.016297       1479.7          0x864dfe0
20.017961       1479.7          0x864dfe0
20.018528       666.5           0x86103b0
20.021289       1479.7          0x864dfe0
20.021875       358.5           0x86624b8
20.024617       1479.7          0x864dfe0
20.025203       359.0           0x86624b8
20.027945       1479.7          0x864dfe0
20.028512       667.0           0x86103b0
20.031273       1479.7          0x864dfe0
20.031859       359.5           0x86624b8
20.033504       667.5           0x86103b0
20.036265       1479.7          0x864dfe0
20.036851       360.0           0x86624b8
20.038496       668.0           0x86103b0
20.041257       1479.7          0x864dfe0
20.041843       360.5           0x86624b8
20.043488       668.5           0x86103b0
20.046249       1479.7          0x864dfe0
20.046835       361.0           0x86624b8
20.048499       361.5           0x86624b8
20.051241       1479.7          0x864dfe0
20.051827       362.0           0x86624b8
20.053491       362.5           0x86624b8
20.056233       1479.7          0x864dfe0
20.056819       363.0           0x86624b8
20.059561       1479.7          0x864dfe0
20.060128       669.0           0x86103b0
20.062889       1479.7          0x864dfe0
20.064553       1479.7          0x864dfe0
File attachments: 
첨부파일 크기
Plain text icon poo.txt153.5 KB
kalstein의 이미지

3가지 정보가 들어가는 구조체 하나 만든담에...

std::vector 같은것에 쭉 집어넣고...

std::sort 함 때려주믄 될듯? 물론 비교 함수는 custom으로 짜셔야죠 ^^


------------------------------------------
Let`s Smart Move!!
http://kalstein.tistory.com/

klara의 이미지

간단하게 하자면 map의 map을 이용하면되겠네요.

실제론 시간과 값은 소수니까 double이고 포인터는 정수니까 int로 쓰겠지만, 편의상 구분하기위해서 시간의 자료형을 time, 값의 자료형을 value, 포인터의 자료형을 pointer라고 하면

std::map<pointer, std::map<time, value> > values;

정도로 선언하여서 예를 들어 한줄 입력 받았더니 20.000243 357.0 0x86624b8 라고 하면

values[0x86624b8][20.000243] = 357.0;

처럼 값을넣으면 될것 같습니다.

혹시 시간이 중복될수도 있으면 std::map<time, value> 대신에 std::multimap<time, value> 을 이용하시면 되겠구요...

map은 자동으로 정렬되어서 들어가니까, 입력이 끝나면 전부 정렬된 상태일테구요...

대신 인덱스 접근은 안되니까 처음부터 값을 가져올려면 반복자를 쓰셔야겠죠.

아님 다 입력받은 담에 std::vector 등으로 복사하셔도 될테구요.

자료 갯수가 적으면 vector로 넣고 정렬 알고리즘을 이용해도 될텐데 갯수가 많으면 map을 두개 이용하는 방법이 더 빠를것 같습니다.

s.choi의 이미지

음.. 우선 두분 코멘트 감사합니다.
제가 C++ 벡터 사용하는 방법을 몰랐었는데,
이 기회에 한번 공부해보고 프로그램 만들어 봐야겠네요.

완성되면, 여기에 포스팅 할테니 한번 봐주세요. :-)

재차 감사드립니다.

s.choi의 이미지

아래와 같이 코드를 적어봤는데요...
그 다음 어떻게 해야될지... 도움 주시면 감사드리겠습니다.
(C++ 파일 어태치 했구요...)

#include
#include
#include
#include
#include

using namespace std;

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

ifstream fin (argv[1]);
ofstream fout;
string nil, hex;
double time, val;

map > values;
//map >::iterator iter;

if(fin.is_open()) {
while (!fin.eof()) {
fin >> nil;
fin >> nil;
fin >> val;
fin >> time;
fin >> hex;

values[hex][time] = val;
}
fin.close();
} else {
cout << "error opening file!" << endl;
exit (1);
}

int size = values.size();
cout << "size: " << size << endl;

return 0;
}

데이터 화일은 첨부시켰구요..
데이터 화일로 부터 하고자 하는 일은,
각 포인터 값에 따라 각각 다른 파일을 만들고자 하는 일입니다.

첫번째 두번째 컬럼은 그냥 버리는 컬럼이구요,
세번째 컬럼은 특정 값을 나타내는 것이고,
네번째 컬럼은 시간값이고,
다섯번째 컬럼이 포인터 값이에요.

즉, 포인터 값에 따라 시간순서대로 값들을 주욱 정렬하는 일을 하고자 합니다.

도움 주시면 너무너무 감사드릴께요~~~

댓글 첨부 파일: 
첨부파일 크기
Plain text icon foo.txt1.91 MB
Plain text icon map.cc_.txt641바이트
klara의 이미지

이제 s.choi님께서하고 싶은걸 하시면 됩니다. map은 항목을 추가할때마다 정렬되어서 들어가있기 떄문에 이미 정렬은 끝나있는 상태입니다.

예를 들어 포인터와 시간 순서대로 값을 출력하고 싶다면

	for (map&lt;string, map&lt;double, double&gt; &gt;::const_iterator ptrIt = values.begin(); ptrIt != values.end(); ++ptrIt)
		for (map&lt;double, double&gt;::const_iterator it = ptrIt-&gt;second.begin(); it != ptrIt-&gt;second.end(); ++it)
			cout &lt;&lt; "pointer: " &lt;&lt; ptrIt-&gt;first &lt;&lt; " time: " &lt;&lt; it-&gt;first &lt;&lt; " value: " &lt;&lt; it-&gt;second &lt;&lt; endl;

이런식이되겠죠.

s.choi의 이미지

감사합니다! 도와주신 덕분에 기본적인 문제는 해결 되었습니다.

다만, 포인터에 별로 결과값을 다른 파일에 저장을 하고 싶은데, 이 부분을 어떻게 하는지 혹시 좋은 방법 알고 계시면 한수만 더 배워보고 싶습니다.

제가 완성한 코드는 첨부로 넣었구요...
(데이터 파일은 위의 foo.txt 파일을 그대로 사용하고자 합니다.)

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <map>
 
using namespace std;
 
int main (int argc, char *argv[]) {
 
    if (argc < 2) {
        cout << "Usage: ./map [file]" << endl;
        exit (0);
    }
 
    ifstream fin (argv[1]);
    ofstream fout;
    string nil, hex;
    double time, val;
 
    map <string, map <double, double> > values;
    map <string, map <double, double> >::iterator pitr;
    map <double, double>::iterator itr;
 
    if(fin.is_open()) {
        while (!fin.eof()) {
            fin >> nil;
            fin >> nil;
            fin >> val;
            fin >> time;
            fin >> hex;
 
            values[hex][time] = val;
        }
        fin.close();
    } else {
        cout << "error opening file!" << endl;
        exit (1);
    }
    int size = values.size();
    cout << "size: " << size << endl;
 
    for (pitr = values.begin(); pitr != values.end(); ++pitr) {
        for (itr = pitr->second.begin(); itr != pitr->second.end(); ++itr) {
            cout << " pointer: " << pitr->first;
            cout << " time: " << itr->first;
            cout << " value: " << itr->second << endl;
        }
    }
 
 
    return 0;
}

다시한번 도움에 깊은 감사드립니다!!
:-)

댓글 첨부 파일: 
첨부파일 크기
Plain text icon map_cc.txt1016바이트
klara의 이미지

음...왠지 map이 '연관 컨테이너'라는 것을 이해하고 못하고 계신느낌이 드는데요(아니라면 죄송합니다), stl을 소개하고 있는 책이나 인터넷에서 간단하게라도 살펴보시는게 좋을 것 같습니다.
cout 대신에 fout을 쓰는 정도외에는 똑같습니다. 예를 들면

	for (map&lt;string, map&lt;double, double&gt; &gt;::const_iterator ptrIt = values.begin(); ptrIt != values.end(); ++ptrIt) {
		fout.open(ptrIt-&gt;first.c_str());
		for (map&lt;double, double&gt;::const_iterator it = ptrIt-&gt;second.begin(); it != ptrIt-&gt;second.end(); ++it)
			fout &lt;&lt; "time: " &lt;&lt; it-&gt;first &lt;&lt; " value: " &lt; it-&gt;second &lt; endl;
		fout.close();
	}
같이 되겠지요.
s.choi의 이미지

아마도 제가 확실하게!! 이해하지 못했던 부분이 있엇던 것 같습니다. 인터넷 자료와 책을 참고하여 map 에 대해서 다시 살펴보았습니다. 그런데, 제가 추가로 질문드린 부분은 map 부분이라기 보다는 file stream 에 관련된 부분이 아닌가 싶습니다. (아니면 스트링?? ㅠ.ㅠ)

즉, 결과값을:
result_1.xg
result_2.xg
result_3.xg
.
.
.

이런식 파일네임으로 개별 저장하고 싶어서 아래와 같이 수정하여 보았는데, 한 부분에서 에러가 나고 있는데, 이유를 잘 모르겠습니다. 어떻게 해야할지 힌트 주시면 너무 감사드릴께요. (지금껏 제 질문 쓰레드에 친절히 답변해 주셔서 너무 감사감사 드리고 있어요...) 많은 도움이 되었습니다.

#include <iostream>
#include <fstream>
#include <string>
#include <map>
 
using namespace std;
 
int main (int argc, char *argv[]) {
 
    if (argc < 2) {
        cout << "Usage: ./map [file]" << endl;
        exit (0);
    }
 
    ifstream fin (argv[1]);
    ofstream fout;
    string nil, hex;
    double time, val;
 
    map <string, map <double, double> > values;
    map <string, map <double, double> >::iterator pitr;
    map <double, double>::iterator itr;
 
    if(fin.is_open()) {
        while (!fin.eof()) {
            fin >> nil;
            fin >> nil;
            fin >> val;
            fin >> time;
            fin >> hex;
 
            values[hex][time] = val;
        }
        fin.close();
    } else {
        cout << "error opening file!" << endl;
        exit (1);
    }
 
    int i;
    int size = values.size();
    string str[size];
 
    for (i=0, pitr=values.begin(); i<size, pitr != values.end(); ++pitr, ++i) {
        str[i].append("result_");
        //str[i].append((string)i.c_str()); // <--- error occurs here!!
        str[i].append(".xg");
        //cout << str[i] << endl;
        fout.open(str[i].c_str());
 
        for (itr = pitr->second.begin(); itr != pitr->second.end(); ++itr) {
            fout << itr->first << "\t" << itr->second << endl;
        }
        fout.close();
    }
 
    return 0;
}

여러가지 네트워크 실험을 하면서 얻은 결과치를 서로 다른 파일에 저장하고 gnuplot 으로 플로팅해서 성능 비교 분석 평가하는 일들을 하다가 발생한 일인데, 혼자서 해결하지 못하다가 xylosper 님 도움 받아가면서 거의 다 해결 한 듯 합니다.

고맙습니다~

s.choi의 이미지

이리저리 찾아보니까 integer 를 string 으로 type cast 하는 방법을 알아봤는데요, std::stringstream 을 이용하는 방법이 있더군요. 그래서 아래와 같이 삽질을 해 봤는데, result_1.xg 까지는 잘 나오는데, 그 다음이 안만들어지는건 정말 왜 그런지...

참, 간단한것 같은데, 속 썩이네요. ㅠ.ㅠ
어디가 잘못 된건지 한번 봐주시면 고맙겠습니다.

#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <map> 
 
using namespace std;
 
int main (int argc, char *argv[]) {
 
    if (argc < 2) {
        cout << "Usage: ./map [file]" << endl;
        exit (0);
    }   
 
    ifstream fin (argv[1]);
    ofstream fout;
    string nil, hex;
    double time, val;
 
    map <string, map <double, double> > values;
    map <string, map <double, double> >::iterator pitr;
    map <double, double>::iterator itr;
 
    if(fin.is_open()) {
        while (!fin.eof()) {
            fin >> nil;
            fin >> nil;
            fin >> val;
            fin >> time;
            fin >> hex;
 
            values[hex][time] = val;
        }
        fin.close();
    } else {
        cout << "error opening file!" << endl;
        exit (1);
    }
 
    int i;
    int size = values.size();
    string str[size], index;
    stringstream sstr;
 
    for (i=0, pitr=values.begin(); i<size && pitr != values.end(); ++pitr, ++i) {
        sstr << i+1;
        sstr >> index;
        cout << index << endl;     // <-- why this just prints four 1s??
        str[i].append("result_");
        str[i].append(index.c_str());
        str[i].append(".xg");
        fout.open(str[i].c_str());
 
        for (itr = pitr->second.begin(); itr != pitr->second.end(); ++itr) {
            fout << itr->first << "\t" << itr->second << endl;
        }
        fout.close();
    }
 
    return 0;
}

이 코드로 위의 foo.txt 돌리면

result_1.xg 화일 하나 밖에 안만들어져요... ㅡ.ㅡ;;;

s.choi의 이미지


음...

stringstream sstr;

이 부분을 for loop 안으로 집어 넣으니까 되네요...
휴~ 참으로 긴 시간동안 답변 달아주시고 관심 가져주셔서 감사합니다.

근데, 저 부분을 stringstream 말고 좀더 elegant 한 방법으로 처리하는 방법은 없을까요? 후후~

아무튼 xylosper 님의 개인시간 많이 뺐어서 너무 죄송하고, 그 동안 답변 큰 도움 되었습니다.

다시한번 감사드립니다.

s.choi의 이미지


그 동안 수 차례에 걸쳐서 xylosper 님의 힌트를 받고서 아래와 같은 프로그램을 작성하게 되었습니다.

원하는 일은, 본 쓰레드 위쪽에 있는 foo.txt 파일을 가지고,
서로다른 포인터가 가지는 값을 각각 result_1.xg, result_2.xg, result_3.xg, result_4.xg 와 같은 파일로 따로 분류/저장 하고자 하는 일입니다. 처음에는 간단하게 awk 으로 해 보려고 했는데, 포인터가 딱 4개 혹은 8개 씩 정해진게 아니라, 어떨때는 100개도 될 수 있고, 어떨때는 2개도 될 수 있고 등등 포인터가 몇개가 될지 모르는 상황이구요... 그 개별 포인터 별로 시간별로 특정 값을 주우우욱 가지게 되는 결과파일이 바로 foo.txt 입니다.

이 일을 해결 하는데 있어서 핵심 내용은 C++의 map 을 이용하는 것이었고, 여기서는 map of map 을 이용했습니다.

./map foo.txt result

라고 실행하면,
result_1.xg
result_2.xg
result_3.xg
result_4.xg
.
.
.
와 같이 결과치를 분류해서 저장하게 됩니다.

그럼 코드는

/*
 * Copyright(c) 2005-2008 University College London
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright 
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright 
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. Neither the name of the University nor of the Laboratory may be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE
 *
 * $Id: map.cc 98 2008-04-08 22:36:34Z soohyunc $
 */
 
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <map>
 
using namespace std;
 
// print instruction
void print_instruction () {
    cout << "Usage: ./map [file] [option]" << endl;
    exit (0);
}
 
// main body
int main (int argc, char *argv[]) {
 
    // print instruction
    if (argc < 3) {
        print_instruction();
    }
 
    ifstream fin (argv[1]); // input file stream
    ofstream fout;          // output file stream
    string nil, hex, option;
    double time, val;
 
    option = argv[2];
 
    // map declaration
    map <string, map <double, double> > values;
 
    // iterator declaration
    map <string, map <double, double> >::iterator pitr;
    map <double, double>::iterator itr;
 
    if(fin.is_open()) {
        while (!fin.eof()) {
            fin >> nil;     // do not need this column
            fin >> nil;     // do not need this column
            fin >> val;     // actual value
            fin >> time;    // time stamp
            fin >> hex;     // pointer value
 
 
            // record map value
            values[hex][time] = val;
        }
        fin.close();    // close file
    } else {
        cout << "error opening file!" << endl;
        exit (1);
    }
 
    int i;
    int size = values.size();   // map size (no. of pointers)
    string str[size], index;    // for the use of file names 
 
    for (i=0, pitr=values.begin(); i<size && pitr!=values.end(); ++pitr, i++) {
 
        // string stream (to change integer to string)
        stringstream sstr;      // string stream declaration
        sstr << i+1;            // index starts from 1
        sstr >> index;          // convert integer to string
        str[i].append(option.c_str());  // start file name with "option"
        str[i].append("_");
        str[i].append(index.c_str());   // append thread number
        str[i].append(".xg");           // end with ".xg"
 
        fout.open(str[i].c_str());  // create a file and open to write
 
        // recording values
        for (itr = pitr->second.begin(); itr != pitr->second.end(); ++itr) {
            fout << itr->first << "\t" << itr->second << endl;
        }
 
        fout.close();   // close file
    }
 
    return 0;
}

이렇게 되었습니다. 자세한 내용은 코드 안에 커멘트로 달아두었습니다.
첨부로 코드를 붙입니다.

코드를 잘 만들어서 공개하는 것이 절대!!!! 아닙니다.
혹시 저와 비슷한 일로 괜히 시간 뺐기지 마시라고 올려둡니다.
(물론 여기 고수님들께선 이런일로 시간 별로 안 뺐기시겠지만... ㅡ.ㅡ;;;)

다시한번 xylosper 님께 감사드리구요, 다른 커멘트 있으시면 환영!! 합니다. :-)

이상입니다.

댓글 첨부 파일: 
첨부파일 크기
Plain text icon map_cc.txt3.45 KB
klara의 이미지

기왕 stringstream을 쓰신다면 그냥 그걸 계속 쓰시는게 좋지 않을까요?

        stringstream sstr;      // string stream declaration
        sstr << i+1;            // index starts from 1
        sstr >> index;          // convert integer to string
        str[i].append(option.c_str());  // start file name with "option"
        str[i].append("_");
        str[i].append(index.c_str());   // append thread number
        str[i].append(".xg");           // end with ".xg"
 
        fout.open(str[i].c_str());  // create a file and open to write

대신에
        stringstream sstr;
        sstr << option << '_' << i+1 << ".xg";
        fout.open(sstr.str().c_str());  // create a file and open to write

그리고 string에 string은 덧붙일때는 char *로 꺼낼 필요가 없습니다. str[i].append(option.c_str()) 보다는 str[i].append(option)으로 충분하고, 연산자 오버로드도 되있으니까 보통은 str[i] += option 처럼 쓰죠...
반복해서죄송합니다만, STL에 대해서 알고리즘까진 안보더라도 string이나 vector같은 컨테이너들에 대한 사용법정도는 훓어보시는게 좋을 것 같습니다.
s.choi의 이미지


그런 방법이 있었군요.. string stream 을 사용하면 integer 를 따로 string 으로 바꿀 필요도 없어지겠네요...

이렇게 시간 내 주시고 친절히 알려주셔서 감사합니다.

STL 사용법을 보긴 했는데, 너무 대충 봤나봐요.. 다시한번 꼼꼼이 살펴보겠습니다.

^_^

꾸벅~

doldori의 이미지

수고하셨습니다. :-)
몇 가지 말씀드리죠.

1. input file stream의 사용법

        while (!fin.eof()) {
            fin >> nil;     // do not need this column
            fin >> nil;     // do not need this column
            fin >> val;     // actual value
            fin >> time;    // time stamp
            fin >> hex;     // pointer value
 
            // record map value
            values[hex][time] = val;
        }

while loop의 탈출 조건으로 eof()를 쓰는 것은 올바른 방법이 아닙니다.
loop 내부에 입력된 값을 cout으로 출력하는 루틴을 넣어보면 마지막 줄을 두 번 읽는 것처럼 보일 것입니다.
그런데 map에 저장하면서 이것이 묻혀버린 것이죠.
대부분의 경우 eof()는 사용할 필요가 없고 다음과 같은 방법으로 족합니다.
        while (fin >> nil >> nil >> val >> time >> hex) {
            // record map value
            values[hex][time] = val;
        }

그리고 파일로부터 직접 값을 입력받는 것은 별로 안전한 방법이 아닙니다.
중간에 잘못된 내용이 있으면 그 이후의 입력은 모두 잘못되니까요.
보통은 다음과 같이 string으로 한 줄을 읽은 후 istringstream으로부터 입력받습니다.
그러면 잘못된 내용이 있는 줄만 건너뛰고 그 다음은 계속 정상적으로 진행할 수 있습니다.
string line;
while (getline(fin, line))
{
    istringstream is(line);
    if (is >> nil >> nil >> val >> time >> hex) { // if input is valid
        // record map value
        values[hex][time] = val;
    }
}

2. string str의 선언

    int size = values.size();   // map size (no. of pointers)
    string str[size], index;    // for the use of file names 

이것은 표준 C++ 코드가 아닙니다. 배열 크기는 컴파일 타임에 정해지는 상수이어야 하는데
size는 런타임에야 알 수 있으니까요. 이 코드는 gcc의 확장기능에 의존하므로 이식성이 없는 것입니다.
사실 이 경우에는 str을 배열로 선언할 필요도 없어보입니다.
    // int size = values.size();  // not necessary
    for (i=1, pitr=values.begin(); pitr!=values.end(); ++pitr, i++) {
        // string stream (to change integer to string)
        stringstream sstr;      // string stream declaration
        sstr << i;
        sstr >> index;          // convert integer to string
 
        string filename(option);  // start file name with "option"
        filename += "_";
        filename += index;   // append thread number
        filename += ".xg";           // end with ".xg"
 
        fout.open(filename.c_str());  // create a file and open to write
 
        // ...
    }

3. 숫자를 string으로 바꾸는 법

기본적으로는 지금 하신 것처럼 stringstream을 이용합니다.
그런데 이것이 좀 번거롭죠. 이럴 때 쓰기 위해 boost라는 라이브러리에서 lexical_cast라는 함수를 제공합니다.
이 역시 stringstream으로 구현한 것입니다.
template으로 되어 있어 operator >>, operator << 가 정의된 형끼리는 자유롭게 변환할 수 있습니다.

#include < boost/lexical_cast.hpp >
 
    for (i=1, pitr=values.begin(); pitr!=values.end(); ++pitr, i++) {
        string filename(option);  // start file name with "option"
        filename += "_";
        filename += boost::lexical_cast< string >(i);   // append thread number
        filename += ".xg";           // end with ".xg"
 
        fout.open(filename.c_str());  // create a file and open to write
 
        // ...
    }

ps. 쓰고 나서 보니까 3번은 xysloper님이 말씀하신 방법이 훨씬 나아 보이는군요.

modestcode의 이미지

Quote:

이 코드는 gcc의 확장기능에 의존하므로 이식성이 없는 것입니다.

아마 g++ 라고 말하려고 했던 것 같습니다. C에서는 표준이니까요.
s.choi의 이미지


너무너무 x 3000 감사드려요...!! 특히, 1번과 2번 항목은 많이 도움되었어요.. 사실 저도 eof()를 while 문과 같이 쓰면서 제일 마지막 줄이 두번 출력되는 문제가 왜 그렇게 된건지 이해를 못하고 있었거든요.. 이제 뭔가 더 클리어하게 알게 된 것 같습니다.

스트링 배열 선언을 제가 한대로 하고서 -Wall 옵션으로 컴파일하면 warning message 가 뜨더라구요. 그래도 일단 컴파일은 됐었던 거라거 무시하고 있었는데, C++ 의 정석은 그 방법대로 하면 안되는 것이었군요??

제 코드를 xylosper 님과 그리고 doldori 님의 말씀대로 수정하였습니다.

완성된 코드와 샘플 trace file을 제일 아래에 올려두겠습니다.

xylosper 님, 그리고 마지막에 좋은 힌트 주신 doldori 님께 다시한번 감사드립니다. :-)

s.choi의 이미지


질문하는 김에 한가지만 더 여쭤볼께요...

말씀하시기로는, 아래 코드면 충분 하다고 하셨는데,,,,,

        while (fin >> nil >> nil >> val >> time >> hex) {
            // record map value
            values[hex][time] = val;
        }

만약, input 파일에 오류가 생겨서, 원래는 5개의 값이 input 으로 들어와야 되는데, 뭐 4개 값만 들어왔다던지, 아니면 6개 값이 들어왔다고 하면.... 그에 해당하는 input 은 받지 않는 방법이 있을까요?

도움말씀 감사드립니다~

klara의 이미지

doldori님께서 그래서 바로 읽어들이기보다는 getline을 이용해서 한줄씩 읽어서 변수로 넣는 예도 적어주셨습니다.

s.choi의 이미지

네, getline 으로 한줄씩 읽는 것은 맞는데, 한줄에 4가지 아이템이 있는지 혹은 6가지 아이템 (혹은 그 이상)이 있는지를 판별 해주지는 못하더라구요.

아래의 부분 코드에서 말인데요...

    if(fin.is_open()) {
        while (getline(fin, sline)) {
            istringstream isOk(sline);
 
            // check if input is valid
            if (isOk >> nil >> nil >> time >> val >> hex) {
                values[hex][time] = val; // record map value
            }
        }
        fin.close();    // close file
    } else {
        cout << "error opening file!" << endl;
        exit (1);
    }

getline 으로 읽기는 읽었는데, 저는 정확히 5가자 아이템이 있는 라인만 input 으로 받고 싶거든요... 위의 코드로는 6개의 아이템 이런 라인도 입력이 되고 이상하게 꼬이고 그러더라구요??

klara의 이미지

그런 경우라면 읽어들인 라인을 파싱하는 수밖에 없을 듯합니다.
s.choi님의 경우라면 읽어들인 sline을 공백문자로 쪼개준후, 갯수를 체크하거나 추가로 필요한 것을 체크한다음 valid한 데이터만 기록하도록요...

s.choi의 이미지


그쵸... ^_^ 제가 바로 그 짓을 하려고 하는데... "공백문자로 쪼개서 하나하나씩 인풋 시키는 작업"
방법을... 잘..... ㅠ.ㅠ

C++ 시작한지가 얼마 안되나서 내공이 많이 부족합니다.. ㅡ.ㅡ;;

klara의 이미지

이미 공백문자로 쪼개서 넣는 것을 하고 계십니다.
istringstream 으로 읽고 있으니까요.

예를 들어 갯수만 검사하면 된다면

istringstream isOk(sline);
vector&lt;string&gt; strs;
string str;
while(isOk >> str)
    strs.push_back(str);
if (strs.size() == 5) {
    //원하는 작업...
}

이런식으로 하면되겠죠.

doldori의 이미지

xysloper님의 말씀처럼 공백을 기준으로 파싱할 수도 있고
다음과 같은 방법도 생각할 수 있겠습니다.
한 줄에 반드시 2개의 데이터만 있어야 할 경우

int data1, data2;
string sentry;
 
istringstream is("1 2");
if ((is >> data1 >> data2) && !(is >> sentry))
    cout << "valid data";
 
is.clear();
is.str("3 4 5 ");
if ((is >> data1 >> data2) && (is >> sentry))
    cout << "too many data";
 
is.clear();
is.str("6.0 a");
if (!(is >> data1 >> data2))
    cout << "insufficient or invalid data";

sentry의 역할은 2개의 데이터를 읽은 후 그 뒤에 공백문자 이외의 내용이 있는지 검사하는 것입니다.
유효한 데이터라면 sentry의 입력은 실패해야 하는 것이지요.
무한포옹의 이미지

참고 삼아 드리는 말씀인데
sort +2 poo.txt > poo.new.txt
로 하면 안되는 것인가요?
포인터 별로 나누는 건 다시 생각하더라도 말이지요.
--
기왕 하는 거 나누는 거 까지
sort +2  poo.txt > poo.sorted.txt ; for i in `sort -u +2 poo.txt | awk '{print $3}'` ;do egrep $i poo.sorted.txt > $i.result ;done
-------------------------------
인생 뭐 있음!

-------------------------------
== warning 대부분 틀린 얘기입니다 warning ===

s.choi의 이미지


이런 방법도 있군요!!! ^_^
컴터는 배우면 배울수록 즐겁습니다.

좋은 방법인것 같아요~ 감사합니당~~

s.choi의 이미지

간단한 것 같았는데 이리저리 해보다보니 여러가지 기본적인 것들을 많이 놓치고 있었구나 라는 생각이 들었습니다. 그 동안 친절하게 답해주신 xylosper 님께 다시한번 감사드리구요, 마지막에 doldori 님의 기본적인 input file stream, string declaration 에 관한 힌트도 큰 도움이 되었습니다!!

input file은 바로 아래 첨부에 있는 foo.txt 입니다. 코드는 아래에 붙여두겠습니다.

/*
 * Copyright(c) 2005-2008 University College London
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright 
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright 
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. Neither the name of the University nor of the Laboratory may be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE
 *
 * $Id: map.cc 108 2008-04-09 15:02:31Z soohyunc $
 */
 
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <map>
 
using namespace std;
 
// print instruction
void print_instruction () {
    cout << "Usage: ./map [file] [option]" << endl;
    exit (0);
}
 
// main body
int main (int argc, char *argv[]) {
 
    // print instruction
    if (argc < 3) {
        print_instruction();
    }
 
    ifstream fin (argv[1]); // input file stream
    ofstream fout;          // output file stream
    string sline;           // input string line
    string nil, hex, option;
    double time, val;
 
    option = argv[2];
 
    // map declaration
    map <string, map <double, double> > values;
 
    // iterator declaration
    map <string, map <double, double> >::iterator pitr;
    map <double, double>::iterator itr;
 
    if(fin.is_open()) {
        while (getline(fin, sline)) {
            istringstream isOk(sline);
 
            // check if input is valid
            if (isOk >> nil >> nil >> time >> val >> hex) {
                values[hex][time] = val; // record map value
            }
        }
        fin.close();    // close file
    } else {
        cout << "error opening file!" << endl;
        exit (1);
    }
 
    int i;
    int size = values.size();   // map size (no. of pointers)
 
    for (i=0, pitr=values.begin(); i<size && pitr!=values.end(); ++pitr, i++) {
 
        // string stream (to create file names accordingly)
        stringstream sstr;
        sstr << option << '_' << i+1 << ".xg";
        fout.open(sstr.str().c_str());  // create a file and open to write
 
        // recording values
        for (itr = pitr->second.begin(); itr != pitr->second.end(); ++itr) {
            fout << itr->first << "\t" << itr->second << endl;
        }
 
        fout.close();   // close file
    }
 
    return 0;
}

이상입니다~~~!!
:-)

댓글 첨부 파일: 
첨부파일 크기
Plain text icon foo.txt1023.17 KB
s.choi의 이미지

또다시 xylosper님, doldori 님 감사드립니다.
처음에는 _매우_ 간단한 작업인줄 알았는데, 솔찬히 많이 배우고 갑니다. :-)

xylosper 님께서 주신 힌트를 이용하여, 입력 필드값 갯수가 정확히 n 개 일때만 map 에 저장하는 기능을 추가하였습니다. 이 기능이 필요했던 이유는, 입력 파일에 간간히 오류가 섞인 데이터가 포함될 수 있는데, 이렇게 되면 8개의 포인터가 가지는 값을 8개의 서로다른 파일로 저장하는데에 있어서 문제가 발생했습니다. C++의 map 이 데이터를 처리할때 오류가 섞인 line의 필드값까지 map에 집어넣어버려서 실제로는 원하는 8개의 map 이 생기는게 아니라 9개도 생겼다가 10개도 생겼다가 자기 맘대로 되버리는 일이 발생 했기 때문입니다. 따라서, 각 입력 line당 정확히 원하는 필드의 갯수가 있는 line 만 map에 집어 넣기를 원했습니다.

완성된 코드는 ....
여기 링크에서 보실 수 있습니다.

(아래에 cut & paste 해두었구요)

/*
 * Copyright(c) 2008 University College London
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright 
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright 
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. Neither the name of the University nor of the Laboratory may be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE
 *
 * $Id: map.cc 110 2008-04-09 15:32:55Z soohyunc $
 */
 
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
 
using namespace std;
 
// print instruction
void print_instruction () {
    cout << "Usage: ./map [file] [option]" << endl;
    exit (0);
}
 
// main body
int main (int argc, char *argv[]) {
 
    // print instruction
    if (argc < 3) {
        print_instruction();
    }
 
    ifstream fin (argv[1]); // input file stream
    ofstream fout;          // output file stream
    string sline;           // input string line
    string nil, hex, option;
    double time, val;
 
    option = argv[2];
 
    // map declaration
    map <string, map <double, double> > values;
 
    // iterator declaration
    map <string, map <double, double> >::iterator pitr;
    map <double, double>::iterator itr;
 
    if(fin.is_open()) {
        while (getline(fin, sline)) {
            istringstream isOk(sline);
            vector<string> svec;        // vector to copy string stream
            string str;
 
            while (isOk >> str)
                svec.push_back(str);    // push stream to vector
 
            // check vector size 
            // the number of input element should be exactly 5
            if (svec.size() == 5) {
                stringstream tmp;
 
                for (int i = 0; i < 5; i++)
                    tmp << svec.at(i) << " ";
 
                if (tmp >> nil >> nil >> time >> val >> hex) {
                    values[hex][time] = val; // record map value
                }
            }
        }
        fin.close();    // close file
    } else {
        cout << "error opening file!" << endl;
        exit (1);
    }
 
    int i;
    int size = values.size();   // map size (no. of pointers)
 
    for (i=0, pitr=values.begin(); i<size && pitr!=values.end(); ++pitr, i++) {
 
        // string stream (to create file names accordingly)
        stringstream sstr;
        sstr << "trace/" << option << '_' << i+1 << ".xg";
        fout.open(sstr.str().c_str());  // create a file and open to write
 
        // recording values
        for (itr = pitr->second.begin(); itr != pitr->second.end(); ++itr) {
            fout << itr->first << "\t" << itr->second << endl;
        }
 
        fout.close();   // close file
    }
 
    return 0;
}

이렇게 되었습니다.

바뀌어진 부분은, input string stream 을 vector 에 집어넣고, 그 벡터의 사이즈가 원하는 사이즈 일때 (즉, n 개의 element) 일때에만 원하는 작업을 하도록 수정했습니다. 저~~~ 위에 xylosper 님께서 pseudo code 형식으로 적어주신 대로 그대로 하니까, 결과의 차이가 없더라구요. 그래서 vector 에 집어 넣었다가, 일일이 다시 끄집어 내고, 그걸 다시 tmp 라는 string stream 으로 집어 넣어주는 형식으로 만들어 봤습니다.

휴!!!!
고수님들한테는 이런거 하는게 몇십분 정도면 될 일을,,,, 저 혼자서 몇일을 고생했네요...
그래도 xylosper 님과 doldori 님 도움 없었으면, 아마 아직도 헤딩하고 있었을꺼에요... 흑흑~~~~

이제는 정말 final version 이겠죠?? ㅎㅎㅎ

감사합니다!!

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.