タブ(\t)がデリミタのファイルの読み込みがうまくできません。

[戻る]
なおぞう 2016/09/15(木) 14:59:46
よろしくおねがいします。
VS2010 VC++ MFC スタティックライブラリ マルチバイト 共通言語サポートしない
OS Win7 で開発をしております。

データベースに登録するための元データが、タブ区切りで存在します。
そのファイルを読み込んでテーブルへINSERTするわけですが…
以下のようなコーディングをしてみました。

//受け取ったファイル名をオープンする
std::ifstream ifs(s_tablename);
std::string str;

int count = 0;
strTableList.RemoveAll();//タイトル登録リスト初期化
strTableItemList.RemoveAll(); //要素登録リスト初期化

//1レコード読み込む (ループ開始)
while(getline(ifs,str))
{
    std::string tmp;
    std::istringstream stream(str);
    count++;
    strTableItemList.RemoveAll(); //要素登録リスト初期化
    //while(getline(stream,tmp,'\t'))
    while(getline(stream,tmp,','))
    {
        if(count == 1){
            strTableList.Add(tmp.c_str());//フィールド名データ
        }
        else{
            strTableItemList.Add(tmp.c_str());//UPDATE,SELECT,INERT用データ
            }
        }
        int num = strTableList.GetCount();
        int num2 = strTableItemList.GetCount();
    }
}

タイトル行:    name1\tname2\tname3\name4\name5\name6
レコード行(1列目) a1\ta2\ta3\ta4\t\t

name5,name6に対するレコードの内容がないと、6番目の要素を取得できないのです。
numの結果が6
num2の結果が5になってしまいます。
タブが悪いのかと思ってカンマでもやってみましたが同じでした。
コーディング方法が間違っているのでしょうか。

よりよい方法があったら教えていただけますようお願いします。

仲澤@失業者 2016/09/15(木) 17:04:26
>a1\ta2\ta3\ta4\t\t

の最後の[tab]と[tab]の間に何もないので、
戻り値としてのストリームが「何もない」になっているだけなのではないでしょうか。
つまり、tmp.c_str()の型がconst char *のはずなので、0が戻り、
結果Add(0)となるため失敗するのでは。

この様な場合のデバッグでは、
1.関数の入れ子をすべて分解して戻り値をいったん変数に入れなおしてから他の関数に渡
す。
2.各段階で戻り値を吟味する
のようにして、原因を探ります。

なおぞう 2016/09/15(木) 17:35:29
>仲澤@失業者さま
アドバイスありがとうございます。

結局、すごく原始的なコーディングをして動作を実現することにしました。
お恥ずかしいコードですが、以下になります。

CStdioFile fp(s_tablename, CFile::modeRead);
CString strReading;
int count = 0;
   while ( fp.ReadString(strReading) != FALSE ) {    //1行読み込み

    strTableItemList.RemoveAll();
    int strcount = strReading.GetLength();
    char strItem;
    CString strA = "";
   CString strB = "";
    count++;
    for(int i = 0 ; i < strcount+1 ; i++){
    strItem = strReading.GetAt(i);
    strA = strItem;
    if(strA == "\t"){
        if(count == 1){
            strTableList.Add(strB);//フィールド名データ
        }
        else{
            strTableItemList.Add(strB);//UPDATE,SELECT,INERT用データ
        }
        strB = "";
    }
    else{
        strB  = strB + strA;
    }
  }
  if(count == 1){
    strTableList.Add(strB);//フィールド名データ
    }
    else{
    strTableItemList.Add(strB);//UPDATE,SELECT,INERT用データ
    }

    int num = strTableList.GetCount();
    int num2 = strTableItemList.GetCount();

このようなコーディングでは駄目だよ。というアドバイスがありましたらよろしくお願い
します。しばらくクローズしないでご意見お待ちしたいと思います。

ITO 2016/09/16(金) 20:54:54
うーーん、
 やりようですけど、一回、一回テーブルに書込まずに、文字列変数にまとめて
保存してからテーブルに書込む方がいいか知れないです。
CStdioFileを使うなら、なおさら楽だと思います。

なおぞう 2016/09/16(金) 21:14:47
>ITO様
書き込みありがとうございます。
うう。すみません。
具体的にコードを書いていただかないと、文字列変数にした後に、どうやってリストに入
れたらよいのか分かりません…。

strTableList、strTableListとも、CStringArray型です。

ITO 2016/09/19(月) 12:44:08
>strTableList、strTableListとも、CStringArray型です。
うーーん、
専用のクラス作成するかな?
ぐらいですね。

ITO 2016/09/22(木) 13:29:42
うーーん、
僕なら、
1.通常char型配列、CStringArray型を用意
2.読込みルーチンでは、一行データをその配列にそのまま保存。
3.編集ルーチンに移行
4.そこで必要な部分を摘出して、char型→CString型に変換して
  CStringArray型に保存。
5.あとは、strTableList、strTableListにコピーする。

でどうかな?
 CStringArray型に保存しておくまでの作業は、
何処のクラスでも出来るから先に準備しておけるから便利。
スレット化もできるしね。

AR 2016/09/26(月) 17:46:20
 ぱっと見で2個目のソースの問題点は
if(strA == "\t"){
 全角文字の'\t'が含まれる2バイト文字がNGで引っかかります。
 また"\t"と'\t'の違いを意識していないところも大丈夫なのかな?と思います。

 そういった行単位での処理レベルでの問題点へ気づきやすくなるのは、ITOさんのよう
な設計のケースですので私も推奨します。
 あと私なら、UNICODEに変換した上で上記の判定処理などは組みますけどね。

なおぞう 2016/09/28(水) 23:31:35
[[解決]]
遅い夏休みをとっていて反応が遅れてしまい申し訳ありません。

>ITO様
具体例の記述、ありがとうございます。
時間が取れたら(今、夏休みを取ったしわ寄せで死にそうです)そちらの方法も考えてみ
たいと思います。

>AR さま
"\t"のご指摘ありがとうございました。
'\t'です…。


これにて解決とさせてもらいます。

古いスレッドには追加発言できません。