CSVの読み込みについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
山口

CSVの読み込みについて

#1

投稿記事 by 山口 » 11年前

勉強として独自のCSVの読み込みをする処理を記載しております。
かなり近い部分まで来ていると思うのですが、上手く動作させることが出来ません。

それっぽい数値がとれているのですがずれてしまうのと、
vector型に代入する時にピラミッドみたいな形になってしまします。

サイズの取得は出来たのですが、whileで回してる部分がおかしいのですが、どうしても直すことが出来ません。
宜しければで構いませんのでアドバイスを頂けないでしょうか・・・。

■参考サイト

http://scitex.hateblo.jp/entry/2013/02/23/231640

■map_01.csv
※最初の1行目はCSVのサイズを指定しております。2行目からCSVとして格納したいです。

Map[5][3]
0,1,2,3,4
10,11,12,13,14
20,21,22,23,24

■読み込みクラス

コード:

#include <iostream>
#include <fstream>
#include <ostream>
#include <string>
#include <vector>

class MAP_SET{

public:
    unsigned int MAP_X;
    unsigned int MAP_Y;
    
    std::vector< std::vector<int> > values;
    void LoadMapCSV( const char* filename );
};

void MAP_SET::LoadMapCSV( const char* filename ){
    
    NSString* data = [NSString stringWithCString:filename encoding:NSUTF8StringEncoding];
    NSString* path = [[NSBundle mainBundle] pathForResource:data ofType:nil];
    
    const char* mainfest_path = [path fileSystemRepresentation];
    std::fstream ifs;
    
    ifs.open( mainfest_path, std::fstream::in );
    if ( !ifs.is_open() ) {
        return;
    }
    
    std::string is_data;
    bool x_y = false;
    
    // サイズを取得
    while ( is_data != "\n" ) {
        is_data = ifs.get();
        
        if( is_data == "[" ){
            std::string numdata;

            while ( !ifs.eof() ) {
                is_data = ifs.get();
                if(is_data == "]"){
                    break;
                }
                numdata += is_data;
            }
            
            if( !x_y ){
                this->MAP_X = (unsigned int)atoi( numdata.c_str() );
                x_y = true;
            }else{
                this->MAP_Y = (unsigned int)atoi( numdata.c_str() );
            }
        }
    }
    
    std::string numdata;
    int p;
    
    printf( "%d %d", this->MAP_X, this->MAP_Y );
    
    while( getline(ifs, numdata) ){
        
        if( ( p = numdata.find("//")) != numdata.npos ) continue;
        std::vector<int> inner;
        
        while( (p = numdata.find(",")) != numdata.npos ){

            //printf( "%s,",  numdata.substr(0, p).c_str() );
            
            numdata = numdata.substr(p+2);
            inner.push_back((int)atoi(numdata.substr(0, p).c_str()));
            
            this->values.push_back( inner );
        }
        
        //printf( "\n" );
        
    }
    
    for( unsigned int y = 0; y < values.size(); y++ ){
        for( unsigned int x = 0; x < values[y].size(); x++ ){
            printf( "%d,",  this->values[y][x] );
        }
        printf( "\n" );
    }

        
    ifs.close();
}



//// 出力結果

0,
0,0,
0,0,0,
0,0,0,0,
1,
1,2,
1,2,3,
1,2,3,4,
1,
1,2,
1,2,3,
1,2,3,4,

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: CSVの読み込みについて

#2

投稿記事 by みけCAT » 11年前

74行目の

コード:

this->values.push_back( inner );
を下に移動し、内側のwhile文の外、外側のwhile文の中に置いてください。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

山口

Re: CSVの読み込みについて

#3

投稿記事 by 山口 » 11年前

凄い早いアドバイスありがとうございます!!
ピラミッドになるのはそこがおかしかったのですね。

確かにコンマまでの分をどんどん減らして行ってたので、
それでピラミッドのように見えていたのですか。

かなり近い所まできてるとは思うのですが、
現在のソースではCSVの値とずれが生じてしまっております。

2個目のwhile文のどこかがおかしいと思うのですが、お分かりになりませんでしょうか。

■出力結果

0,0,0,0,
10,1,2,3,
20,1,2,3,

コード:

 while( getline(ifs, numdata) ){
        
        if( ( p = numdata.find("//")) != numdata.npos ) continue;
        std::vector<int> inner;
        
        while( (p = numdata.find(",")) != numdata.npos ){

            //printf( "%s,",  numdata.substr(0, p).c_str() );
            
            inner.push_back((int)atoi(numdata.substr(0, p).c_str()));
            numdata = numdata.substr(p+2);
            
            
        }
        
        //inner.push_back((int)atoi(numdata.c_str()));
        this->values.push_back( inner );

        //printf( "\n" );
        
    }

かずま

Re: CSVの読み込みについて

#4

投稿記事 by かずま » 11年前

山口 さんが書きました: かなり近い所まできてるとは思うのですが、
現在のソースではCSVの値とずれが生じてしまっております。
',' を見つけてから atoi() ですか?
それでは、行の先頭の数値は読み取れません。
',' を見つけて、2文字先から atoi() ですか?
それでは、正しい数値は読み取れません。

以下はおまけ。

コード:

void MAP_SET::LoadMapCSV(const char* filename)
{
    std::fstream ifs(filename);;
    if (!ifs) return;
    
    std::string line;
    if (!getline(ifs, line)) return; 

    size_t pos = line.find('[');
    MAP_X = atoi(line.c_str() + pos + 1);
    pos = line.find('[', pos + 1);
    MAP_Y = atoi(line.c_str() + pos + 1);
    
    while (getline(ifs, line)) {
        std::vector<int> inner;
        for (pos = 0; ; pos++) {
            inner.push_back(atoi(line.c_str() + pos));
            pos = line.find(',', pos + 1);
            if (pos == std::string::npos) break;
        }
        this->values.push_back(inner);
    }
    
    printf("%d %d\n---\n", MAP_X, MAP_Y);
    for (size_t y = 0; y < values.size(); y++) {
        for (size_t x = 0; x < values[y].size(); x++)
            printf("%d,",  this->values[y][x]);
        printf("\n");
    }
}

山口

Re: CSVの読み込みについて

#5

投稿記事 by 山口 » 11年前

ありがとうございます!!
ヒントと言うより答えまで助かります(汗)

わからない変数の型が出てきたので、解析させて下さい。
上手くいかなかった原因も知りたいので、自分でも直せるように頑張ってみます。

かずま

Re: CSVの読み込みについて

#6

投稿記事 by かずま » 11年前

かずま さんが書きました: ',' を見つけてから atoi() ですか?
それでは、行の先頭の数値は読み取れません。
',' を見つけて、2文字先から atoi() ですか?
それでは、正しい数値は読み取れません。
すみません。これは、山口さんの最初のプログラムに関するコメントでした。
2つめのプログラムだと、次のようになります。

',' を見つけて、そこまでの atoi() ですか?
それでは、行の最後の数値は読み取れません。
',' を見つけて、2文字先から atoi() ですか?
それでは、各数の先頭の数字 1文字が欠けます。

また、次のようにしたほうが、効率がよいでしょう。

コード:

    while (getline(ifs, line)) {
        values.push_back(std::vector<int>()); // 空の vector<int> を追加
        std::vector<int>& inner = values[values.size() - 1];
          // inner は今、追加したばかりの空の vector<int> で、values の中にある
        for (pos = 0; ; pos++) {
            inner.push_back(atoi(line.c_str() + pos));
            pos = line.find(',', pos + 1);
            if (pos == line.npos) break;
        }
        // ここで values.push_back(inner); をしなくてよい。(配列のコピーが不要)
    }

zeek

Re: CSVの読み込みについて

#7

投稿記事 by zeek » 11年前

かずま さんが書きました:

コード:

    while (getline(ifs, line)) {
        values.push_back(std::vector<int>()); // 空の vector<int> を追加
        std::vector<int>& inner = values[values.size() - 1];
          // inner は今、追加したばかりの空の vector<int> で、values の中にある
        for (pos = 0; ; pos++) {
            inner.push_back(atoi(line.c_str() + pos));
            pos = line.find(',', pos + 1);
            if (pos == line.npos) break;
        }
        // ここで values.push_back(inner); をしなくてよい。(配列のコピーが不要)
    }
widen 使った方がスマートかな?

コード:

    while (getline(ifs, line)) {
        values.push_back(std::vector<int>());
        std::istringstream iss(line);
        std::string item;
        while (getline(iss, item, iss.widen(','))) {
            values.rbegin()->push_back(atoi(item.c_str()));
        }
    }

かずま

Re: CSVの読み込みについて

#8

投稿記事 by かずま » 11年前

zeek さんが書きました: widen 使った方がスマートかな?
単に ',' で済むのに iss.widen(',') を使うのはどうしてですか?
setlocale() は要らないのですか?

istringstream を使うのなら、こんな風にも書けます。

コード:

    while (getline(ifs, line)) {
        values.push_back(std::vector<int>());
        std::istringstream iss(line);
        int val;
        while (iss >> val) {
            values.rbegin()->push_back(val);
            char c; iss >> c;  // skip comma
        }
    }

山口

Re: CSVの読み込みについて

#9

投稿記事 by 山口 » 11年前

色々とありがとうございます!!
理解できない処理が何個かあるのですが、
とりあえず動いて欲しいというのと違い効率を求めるのは凄いですね(汗)
尊敬します。。。

CSVの読み込みだけでもここまで色々なやり方があると思うと
プログラムが楽しくなってきました。
この度はありがとうございました。

zeek

Re: CSVの読み込みについて

#10

投稿記事 by zeek » 11年前

質問されたので回答です。
かずま さんが書きました: 単に ',' で済むのに iss.widen(',') を使うのはどうしてですか?
string を wstring に変更する場合にアルゴリズム部分を変更しなくて済むからです。
いつもは私はこんな感じで書いています。
string と wstring とを切り替える(istringstream と wistringstream 等の切り替えも含む)ヘッダを用意してできるだけ共通の code にしています。

コード:

template<class T>
T toValue(const tstring &obj)
{
    tistringstream iss(obj);
    T val;
    iss >> val;
    return val;
}

    while (getline(ifs, line)) {
        values.push_back(vector<int>());
        tistringstream iss(line);
        tstring item;
        while (getline(iss, item, iss.widen(','))) {
            values.rbegin()->push_back(toValue<int>(item));
        }
    }
かずま さんが書きました: setlocale() は要らないのですか?
規格では setlocale() を実行しなくても "C" locale が設定された状態でプログラムは起動されるとなっていたと思います。
つまり setlocale(LC_ALL,"C"); を実行した状態と同じだったと思います。

zeek

Re: CSVの読み込みについて

#11

投稿記事 by zeek » 11年前

山口 さんが書きました: CSVの読み込みだけでもここまで色々なやり方があると思うと
プログラムが楽しくなってきました。
ご参考までに csv は RFC4180 で規定されています。
http://www.7key.jp/rfc/rfc4180.html
この仕様で読み込むには、もう少し処理が必要になります。
(ここに書かれている code では、RFC4180 を満足できてはいないのでご注意ください)

閉鎖

“C言語何でも質問掲示板” へ戻る