• ベストアンサー

fgetsで読み込んだ値のvector処理

Visual C++ 2008でc++プログラミングの勉強をしています。 ファイルから文字列をfgetsで読み込み、vectorにいれる処理をプログラミングしようとしているのですが、うまくいきません。 input.txt------------------ sumday monday tuesday ------------------------- このような入力ファイルをfgetsで読み込み、各行を文字列としてvectorにpush_backし、 読み込みが終わった後にvectorの全要素をループで出力するというシンプルなものなのですが、以下のようにコーディングしました。 #include <stdio.h> #include <stdlib.h> #include <vector> int main(void){ using namespace std; FILE *fp; fp = fopen("input.txt","r"); std::vector<char *> my_vector; char buff[16]; while(fgets(buff, 256, fp) != NULL) { char copy_of_buff[16]; std::strcpy(copy_of_buff, buff); my_vector.push_back(copy_of_buff); printf("output from fgets..... %s \n", copy_of_buff); } vector<char *>::iterator it = my_vector.begin(); // while( it != my_vector.end() ) // { printf("output from vector.... %s \n", *it); ++it; // } fclose(fp); return 0; } 以下のような出力がなされるものと思っていたのですが、 outputs from fgets .... sunday outputs from fgets .... monday outputs from fgets .... tuesday output from vector .... sunday output from vector .... monday output from vector .... tuesday 実際は以下のように、vectorからの出力分がすべて最後にpush_backした"tuesday"となりました。 outputs from fgets .... sunday outputs from fgets .... monday outputs from fgets .... tuesday output from vector .... tuesday output from vector .... tuesday output from vector .... tuesday fgetsしたあとの処理が問題だと思うのですが、原因がよく分かりません。非常に基本的なことだと思うのですがwebで調べてもいまいちわかりません。 原因が分かる方、よろしくお願いします。

質問者が選んだベストアンサー

  • ベストアンサー
  • chie65535
  • ベストアンサー率43% (8516/19358)
回答No.2

最初のプログラムは「文字列」ではなく「文字列が入っていた場所の先頭アドレス」をプッシュバックしています。 my_vectorが「char *」つまり「文字列が入っている場所の先頭アドレス」を要素に持つと宣言されているのですから、文字列そのものは入れられません。 「中に入っている文字列は毎回違う」のですが、残念ながら「プッシュバックされた文字列の先頭アドレスは毎回同じ」です。 ループでは「&copy_of_buff[0]」が3回プッシュバックされます。 プッシュバックのループを抜けると「文字列」が破棄されますが「メモリには最後にプッシュバックした時の残骸である、最後の文字列が残って」います。 で、ベクター要素を走査すると、毎回「&copy_of_buff[0]」が取り出されます。 copy_of_buffは既に破棄されているので、取り出したアドレスが差すメモリには、最後にそこに入れた文字列の残骸があります。 そして、表示ルーチンでは、その「残骸」を表示する事になります。 修正したプログラムでは、stringをプッシュバックしているので、string(のコピー)がプッシュバックされます。 my_vectorが「string」つまり「文字列そのもの」を要素に持つと宣言されているのですから、文字列そのものが入れられます。 最初のプログラムの char copy_of_buff[16]; を char *copy_of_buff = malloc(16); にすれば、とりあえずは動きます。 これが意図した通りに動く理由は「毎回、新しい領域を確保している」「毎回、新しく確保した領域のアドレスをプッシュバックしている」「表示が終るまで確保したメモリを破棄しないで取ってある」からです。 但し、この修正だけでは「確保した領域を使い終わったのに開放してない」ので、不完全です。 ++it; // を free(*it++); // に変えて、表示し終わったら解放しましょう。

hydrozoa
質問者

お礼

>最初のプログラムは「文字列」ではなく「文字列が入っていた場所の >先頭アドレス」をプッシュバックしています。 >「中に入っている文字列は毎回違う」のですが、残念ながら「プッシュ >バックされた文字列の先頭アドレスは毎回同じ」です。 なるほど。よく分かりました。 やはりC/C++のアドレスなどの概念は(私にとっては)難しいですね。 ループなどの処理と一緒になると、さらにややこしくなりますね。 ありがとうございました。

その他の回答 (2)

  • chie65535
  • ベストアンサー率43% (8516/19358)
回答No.3

最初のプログラムが動かなかった理由は、以下のサンプルを見れば一目瞭然だと思います。 int a; std::vector<int *> my_vector; a = 10; my_vector.push_back(&a); // 10はプッシュされない。プッシュされるのはaのアドレス a = 20; my_vector.push_back(&a); // 20はプッシュされない。プッシュされるのはaのアドレス a = 30; my_vector.push_back(&a); // 30はプッシュされない。プッシュされるのはaのアドレス a = 40; vector<int *>::iterator it = my_vector.begin(); // while( it != my_vector.end() ) // { printf("output from vector.... %d \n", **it); // *itを参照すると「aのアドレス」が取り出される。 // **itを参照すると「aのアドレスが指す中身」が取り出される。 // 「aのアドレスが指す中身」とは「今のaの値」の事。 // 「今のaの値」は40なので「40」しか表示しない。 ++it; // } このプログラムはchar *がint *になっただけで、やっている事は「質問者さんが最初に書いたプログラムと同じ」です。 これで、最初のプログラムがマトモに動かない理由が理解できたと思います。

hydrozoa
質問者

お礼

詳しい解説ありがとうございました。 最初のプログラムの問題が良く分かりました。

  • koko_u_u
  • ベストアンサー率18% (216/1139)
回答No.1

>while(fgets(buff, 256, fp) != NULL) { > char copy_of_buff[16]; > std::strcpy(copy_of_buff, buff); > my_vector.push_back(copy_of_buff); > printf("output from fgets..... %s \n", copy_of_buff); >} copy_of_buff は while ループをまわる度に確保と破棄を繰り返します。 その先頭アドレスを my_vector に格納しているわけですが、当然ループから抜ければ 先頭アドレスの指す先は「無効」です。 動作しているのは、たまたま、あなたの使っているコンパイラが 毎回「同じ場所に」 copy_of_buff の領域を確保し、ループから抜けた時にその領域を そのまま放置しているからでしょう。 C++ で書くなら、文字列は string で、ファイルからの入出力は fstream で行いましょう。

hydrozoa
質問者

お礼

ありがとうございます。 string、fstreamをつかって書き直してみました。(少し出力形式が違いますが) #include <fstream> #include <string> #include <vector> #include <iostream> using namespace std; int main( ) { ifstream ifs("input.txt"); string buf; std::vector<string> my_vector; while(ifs && getline(ifs, buf)) { cout << buf << endl; my_vector.push_back(buf) ; } vector<string>::iterator it = my_vector.begin(); while( it != my_vector.end() ) { cout << *it << endl; ++it; } return 0; } 結果的に自分の望む出力になりましたが、まだ理由がよく分かりません.......................

関連するQ&A

  • vectorの中にmap

    vectorの中にmapを入れて 添字:ノードID [どのノードから来たのか|それまでのコスト] を表現しようと考えています. #include<iostream> #include<vector> #include<map> #include<list> // MACROS #define UNDEF -1 // PROTOTYPE DCLARE void init_path(std::vector<std::map<char, int> >, int size); int main(void) { //source -> source node // //size -> the number of node // //path -> store path infomation // ex: // path[1]: 1 is node id // char : from node id // cost : how cost from source to here // //adj -> show adjacency list eace node int source; int size; std::vector<std::map<char, int> > path; std::vector<std::list<char> > adj; size = 5; std::cout << "before" << std::endl; init_path(path, size); std::cout << "after" << std::endl; std::map<char, int>::iterator it; for(int i = 0; i < size; i++) { it = path[i].begin(); // std::cout << it->first << ":" << it->second << std::endl; } return 0; } void init_path(std::vector<std::map<char, int> > path, int size) { std::map<char, int> init; init.insert( std::map<char, int>::value_type('-', UNDEF) ); for(int i = 0; i < size ; i++) { path.push_back(init); } return; } *結果 before after Segmentation fault となり初期化をする所までは正常に動いたっぽいのですが どこが悪いのかわかりません(おそらくイテレータあたりかと思うのですが・・・・ 具体的にどうしたらいいのか分からないのでご指導ねがいます.

  • STLで、vectorのファイルへの書き出し、読み込み。

    STLで、vectorのファイルへの書き出し、読み込み。 C++ の STL で vector をファイルに書き出し、読み込もうとしておりまして、まずは書き出しで躓いております。 vector<int> testvector; for (i = 0 ; i < 10 ; i++) testvector.push_back( i ); ofstream out("test.dat", ios::out | ios::binary); if (!out) return 1; out.write((vector<int>) testvector, sizeof(testvector)); out.close(); こんな感じのコードですと、 error: no matching function for call to ‘std::basic_ofstream<char, std::char_traits<char> >::write(std::vector<int, std::allocator<int> >, long unsigned int)’ /usr/include/c++/4.2.1/bits/ostream.tcc:173: note: candidates are: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::write(const _CharT*, std::streamsize) [with _CharT = char, _Traits = std::char_traits<char>] とのことです orz. どのようにすれば vector を書き出し、そして読み込むことができるのでしょうか。情報がありましたら是非お寄せください。

  • ファイル読み込みとmap処理

    Visual C++ 2008 Express Edition 環境です。 入力テキストファイルを読み込み、空白で単語を区切り、単語すべてをmapにいれるという処理のプログラムを書こうとしています。 perlでいうところのsplit, 配列へのpushをC++でstrtokとmapでならかけると思いました。 入力ファイルは input1.txt--------------- cat dog mice human mosquito beetle spider ------------------------- プログラムは #include <stdio.h> #include<iostream> #include <map> #include <vector> int main( ) { FILE *input_file1; input_file1 = fopen("input1.txt", "r"); char str[256]; char *token; std::vector<char *> my_vector; // while (fgets(str, 256, input_file1) != NULL) { token = strtok( str , " " ); while( token != NULL ){ my_vector.push_back(token) ; printf("%s\n",token); token = strtok( NULL , " " ); } } printf("starting vector loop\n"); std::vector<char *>::iterator it = my_vector.begin(); // while( it != my_vector.end() ) // { printf("%s\n",*it); ++it; // } fclose(input_file1); return 0; } というふうにしました。 cat dog mice human mosquito beetle spider というような出力がなされるものと思ったのですが、実行してみると mapを使ったループ(全要素)出力は mosquito uito le mosquito beetle spider というふうに出力されてしまいます。 strtokで単語を分ける部分は問題なく出力で確認できるので、問題はmapの作り方やポインタだと思うのですが原因がわかりません。 問題点、解決策がお分かりになる方、よろしくお願いします。

  • Vectorでヒープエラーが出る

    下記のプログラムで、2回目のループを抜けたところでヒープエラーが 出てしまいます。 2回目のループ時にv[0]とv[1]の中のbuffが同じポインタを指してしまうことが原因のようですが、なぜ同じポインタを指してしまうのかがわかりません。 どうしたらこの問題が解決するのでしょうか? 以上、よろしくお願いします。 #include "CBuff.h" main(){ vector<CBuff> v; char *tmpstr; for (int i = 0; i < 3; i++){ if (i == 0){ tmpstr = new char[5]; memcpy(tmpstr, "aaaa", 5); }else if(i == 1){ tmpstr = new char[6]; memcpy(tmpstr, "bbbbb", 6); }else { tmpstr = new char[7]; memcpy(tmpstr, "cccccc", 7); } CBuff cb; cb.setBuff(tmpstr, strlen(tmpstr) + 1); v.push_back(cb); delete [] tmpstr; } } 【CBuff.h】 #pragma once class CBuff{ char *buff; public: CBuff(); ~CBuff(); void setBuff(char*, size_t); }; CBuff::CBuff(){ buff = NULL; } CBuff::~CBuff(){ if (buff != NULL){ delete [] buff; buff = NULL; } } void CBuff::setBuff(char *input, size_t size){ buff = new char[size]; memcpy(buff, input, size); }

  • vectorを使っていたいのですが上手くいきません

    文字列(A)から特定の文字列(B)を抽出しながらvectorにどんどん入れていくプログラムを作成しています。(文字列(B)の位置や長さはわかっているとします。) そこで、文字列(A)から、文字列(B)に相当するアドレスをvectorにどんどん入れているのですが、先pushした値が後からpushした値と全て同じ値になってしまいます。 これは、アドレス渡しが原因と分かってはいるのですが、どう書けば上手くいくかわかりません。 そこでご教授頂きたいと思っています。 自分が書いたプログラムは以下になります。 #include <string.h> #include <stdio.h> #include <stdlib.h> #include <vector> using namespace std; void main(){ char* stringA; vector<char*> result; char temp[16];//文字列Bの大きさ for(int i=0;i<file.size();i++){ strncpy(temp,&buffer[i],16); result.push_back(*temp); //次の文字列Bの位置までインデックスを移動//// while(buffer[i]!=0x0A){ i++; } i++; } }

  • Vectorのポインタが入ったvector

    windows Vista sp1, Visual C++ 2008でC++の勉強をしています。 Vectorへのポインタが入ったvectorを使うプログラムを書いているのですがうまくいかず、困っています。 どういうプログラムかというと、 入力ファイルの">"という記号を区切りとして、その間にある各行をひとまとめのグループとしてvectorにいれます。 さらに各vectorのポインタをべつのvectorに入れます。 最終的に区切りの数だけvectorができ、入力ファイルを読み終わった後に すべてのvectorを"各グループのポインタが入ったvector"からループ処理ですべて出力する、というものです。 /入力ファイル input.txt/ > human cat dog > beetle dragonfly spider > salmon saury catfish > vector1には human cat dog vector2にはbeetle dragonfly spider vector3にはsalmon saury catfish が入り、 別のvectorにそれぞれのvectorのポインタをいれ、 最後にこのvectorをつかって全ファイル内容を出力するというものです。 具体的に書くと、 ">"の区切りごとの各行のstringを入れるvectorとしてeach_vector。 each_vectorのポインタを入れるvectorをvector_of_ptr_each_vectorとします。 ">"を認識するごとに new で each_vectorの領域を確保し、そのポインタをvector_of_ptr_each_vectorに追加していき、">"のない行のstringを each_vectorに入れます。 ファイルの読み込みが終わった後でvector_of_ptr_each_vectorからイテレータを使って各vector(each_vector)の全要素をそれぞれ出力する、というものです。 以下のようにコードを書きました。 #include <fstream> #include <string> #include <vector> #include <iostream> using namespace std; int main( ) { ifstream ifs("input.txt"); string buf; std::vector<string> each_vector; std::vector<std::vector<string> *> vector_of_ptr_each_vector; while(ifs && getline(ifs, buf)) { if(buf[0] == '>'){ std::vector<string>* ptr_eachvector ; ptr_eachvector = new std::vector<string>; each_vector = *ptr_eachvector ; vector_of_ptr_each_vector.push_back(ptr_eachvector) ; } each_vector.push_back(buf) ; } printf("\n output from vector of ptr of vector\n"); std::vector<std::vector<string> *>::iterator it_b = vector_of_ptr_each_vector.begin(); while( it_b != vector_of_ptr_each_vector.end() ) { std::vector<string>::iterator it_c = it_b->begin();    //エラー1 while( it_c != it_b->end() ) //エラー2 { cout << *it_c << endl; ++it_c; } ++it_b; } return 0; } ですが、エラーでビルドされず、 std::vector<string>::iterator it_c = it_b->begin(); の行に関して error C2839: invalid return type 'std::vector<_Ty> **' for overloaded 'operator ->' error C2039: 'begin' : is not a member of 'std::_Vector_iterator<_Ty,_Alloc>' while( it_c != it_b->end() )   の行に関して error C2839: invalid return type 'std::vector<_Ty> **' for overloaded 'operator ->' 1> with 1> [ 1> _Ty=std::string 1> ] error C2039: 'end' : is not a member of 'std::_Vector_iterator<_Ty,_Alloc>' 1> with 1> [ 1> _Ty=std::vector<std::string> , 1> _Alloc=std::allocator<std::vector<std::string> > 1> ] fatal error C1903: unable to recover from previous error(s); stopping compilation というようなエラーが出ます。 vectorのポインタを入れたvectorの扱い、特にイテレータに関して問題があると思うのですが原因が分かりません。 また、new でのeach_vectorの領域確保の方法も怪しいという感じがします。 解決策、アドバイスありましたらよろしくお願いします。

  • C++ vector中のnewしたの文字列の削除の問題

    C++の新人ですが、困っている質問をさせていただきます。 C++のvectorの中にchar型の文字列を格納していて、どうやって、 memory leak が発生しないようにクリアしますか? たとえば、以下の例 std::vector<char *> vc; void pushvc(); int main(){ pushvc(); } void pushvc(){ char* a = new char[10]; char* b = new char[10]; a="sss"; b="bbb"; vc.push_back(a); vc.push_back(b); } よろしくお願いします。

  • C++ STL vectorの使い方

    こんばんは。 C++のstd::vectorに関する質問です。 vectorをポインタ渡しにしたときに メンバにアクセスする方法を知りたいのですが・・・ 以下ソースの☆の部分をどう記述したらよいでしょう? #include <vector> typedef struct test {  char name[10];  char id[2]; } TEST; void funcVectorTest( std::vector<TEST> *a); int main(){  std::vector<TEST> a;  int i;  TEST foo= {"Taro","0"};  a.push_back(foo);  printf("%s",a[0].name);  a.push_back(foo);  printf("%s",a[1].name);  funcVectorTest(&a);//vectorのアドレス渡しテスト  printf("%s", a[2].name);//vectorのアドレス渡し確認  return 0; } void funcVectorTest( std::vector<TEST> *a) {  int i;  int cnt;  TEST *b;  b = new TEST[3];  TEST foo= {"Taro","0"};  a->push_back(foo);  cnt = a->size();  for( i = 0; i < cnt;i++){  //以下でa[i]のnameにアクセスしたいのですがうまくいっていません。  //☆strcpy( b[i].name, a[i]->name );  }  delete[] b; } お分かりになる方、お知恵をお貸し下さい(><) vectorについて最近知ったばかりでいまいち使い方が 分かっていない部分があるので このやり方がまずいということであれば教えていただけると 助かります。 よろしくお願いしますm( _ _ )m

  • C++初心者です。ご指導よろしくお願いします。

    C++初心者です。ご指導よろしくお願いします。 C++で特定の行の値を読み込むプログラムを作っています。 a.txtとb.txtが入力ファイルで、c.txtが出力ファイルです。 a.txtには 237891 193203 1355876 ・ ・ ・ (以下1~5000000の数値がランダムに15000行分) b.txtには 0.333333 0.333333 0.397396 ・ ・ ・ (以下0.333333~0.822222までの数値がランダムに5000000行分) が書いてあって、 c.txtに a.txtの1行目の数値の行に対応するb.txtの値 a.txtの2~ a.txtの3~ ・ ・ ・ (以下15000行分) を出力するプログラムを作りたいと思っています。 以下のように、プログラムを書きましたが、a.txtが10行、b.txtが20行程度の時は問題なく動くのですが、行数が多くなると急に動かなくなります。 charのところを変えたり、offsetのところを変えたりしたのですが、最初の1行を読み込んだところで止まってしまいます。 (buffの値は=237891 no2の値は=237891まで) どのようにすれば動くようになるでしょうか? ご指導よろしくお願いします。 #include <stdio.h> #include <iostream> #include <fstream> #include <cstdlib> #include <cstring> using namespace std; int main(void) { FILE *fp,*fp2,*fp3; char buff[256],buff2[256]; long int offset[100],offset2[100]; long int max,max2; long int no=0; long int no2=0; for(no=1; no<=15000; no++){ fp = fopen("input/a.txt","r"); if(fp == NULL){ cout << "入力ファイルをオープンできません\n"; } for ( max = 0 ; !feof(fp) ; max++ ){ if ( max >= 100 ){ break; } offset[ max ] = ftell( fp ); fgets( buff, sizeof(buff), fp ); } fseek( fp, offset[no - 1], SEEK_SET ); fgets( buff, sizeof(buff), fp ); cout << "buffの値は=" << buff << "\n"; no2 = atoi(buff); cout << "no2の値は=" << no2 << "\n"; fp2 = fopen("input/b.txt","r"); if(fp2 == NULL){ cout << "入力ファイルをオープンできません\n"; } for ( max2 = 0 ; !feof(fp2) ; max2++ ){ if ( max2 >= 100 ){ break; } offset2[ max2 ] = ftell( fp2 ); fgets( buff2, sizeof(buff2), fp2 ); } fseek( fp2, offset2[no2 - 1], SEEK_SET ); fgets( buff2, sizeof(buff2), fp2 ); cout << "buff2の値は=" << buff2 << "\n"; fp3 = fopen("input/c.txt","a"); if(fp2 == NULL){ cout << "入力ファイルをオープンできません\n"; } fprintf(fp3, buff2); strcpy(buff,"0"); strcpy(buff2,"0"); no2=0; cout << "buff2は初期化されて=" << buff2 << "\n"; fclose(fp); fclose(fp2); fclose(fp3); } }

  • C++での戻り値について

    C++で以下のソースを書きました。 どうしてaaaは問題ないのにbbbはだめなのかがわかりません。 どちらも、func1()、func2()で設定した文字列・vectorのポインタを返したいです。 int main() { const char* aaa = NULL; std::vector<const char*>* bbb = NULL; aaa = func1(); bbb = func2(); } const char* func1() { const char* str = NULL; str = "test"; return str; } std::vector<const char*>* func2() { std::vector<const char*>* str2 = NULL; str2->push_back("test2"); str2->push_back("test3"); return str2; } 現在必要に迫られてC++勉強中です。よろしくお願いいたします。

専門家に質問してみよう