• 締切済み

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); } }

みんなの回答

  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.7

すみません。ftellを見落としてました。 > long int offset[100],offset2[100]; ローカル変数(この変数はmain関数内だけで有効なローカル変数です)でこのように宣言されたものは、自動変数といって、スタック領域と呼ばれるメモリ空間に確保されます。 この領域はそんなに大きくありません。Visual C++の標準で1MBだそうです。 100万バイトに500万×4(longのバイト数)は入るはずもありません。 大きな配列を使う場合は、次のような方法があります。 1) ヒープ領域を使う Cならmalloc/calloc, C++ならnew演算子を使います。使い終ったら free / delete[]で解放します。 解説書ではポインタのところに詳しく書いてあるはずです。 2) 静的変数を使う。 静的変数についての詳細は、解説書などを参考に。 static long int offset[5000000],offset2[5000000]; とすると、最初から大きなメモリが使えます。 静的変数にはいろいろ制約があるのですが、今回のケースなら問題ないです。 3) C++限定: 配列と同等の機能を持つクラスを利用する(std::vector等) それから ディスクアクセスは、非常に遅い処理です。 メモリがナノ(10^-9)秒オーダーなのに対し、ディスクのシークタイムはせいぜいミリ秒(10^-3)オーダーです。 速度を求めるなら、アクセスは極力減らすべきです。 このプログラムだと aを1行読み込み→bを全部読み込み(オフセットを取得)→bを1行読み込み→cに書き込み となっています。bの読み込みだけでは(500万行)×1万5千(aの行数)×9文字(1行あたりの文字数)=675GBです。 700GBくらいのハードディスクを丸々コピーするようなものです。 すでにb.txtの読み出す場所は判っている( no2 行目)のですから、そこまで読めばオフセットは判ります。 さらに、この方法だと、forが終った時点でbuff2に所望の行が入っているわけですから、offset2もfseekも必要ありません。 for ( max2 = 0 ; (!feof(fp2)) && (max2 < no2) ; max2++ ){ fgets( buff2, sizeof(buff2), fp2 ); } cout << "buff2の値は=" << buff2 << "\n"; さらに、a.txtのfopen/fcloseをfor(max=...の外に出せば、 a.txt用のoffsetも不要です。 fp = fopen("input/a.txt","r"); for(no=1; no<=15000; no++){ fgets( buff, sizeof(buff), fp ); no2 = atoi(buff); ... /*削除: fclose(fp); */ } fclose(fp); } 以上が省メモリ版です。オリジナルに比べて平均で半分くらいの時間で実行できるはずです。(それでも300GBのコピーくらいの時間ですが)

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.6

ああそうだ, 「fseek の使い方」そのものはこれであってますよ>#4. ちゃんと「ftell で得られる情報」を使ってますから. C++ でいくなら, たとえば #include <fstream> #include <vector> #include <string> #include <iterator> #include <algorithm> using namespace std; int main() { vector<string> data; { ifstream fb("input/b.txt"); string buf; while (getline(fb, buf)) data.push_bacK(buf); } transform(istream_iterator<int>(ifstream("input/a.txt")), istream_iterator<int>(), ostream_iterator<string>(ofstream("input/c.txt", ios_base::app), "\n"), [&data](int x) { return data[x-1]; }); return 0; } のように書けるはず... なんだけど, なぜか GCC 4.5.1 ではコンパイルに失敗する. *_iterator の引数に直接 *stream を入れられない. う~む. もちろん data を vector<double> にすれば, もっと短くなる.

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.5

まあ確かに Perl の方が簡単かも>#4. perl -MFileHandle "FileHandle->new('input/c.txt','a')->print(('', FileHandle->new('input/b.txt', 'r')->getlines)[FileHandle->new('input/a.txt', 'r')->getlines]);" のワンライナーですしね... って, さすがにこれはやりすぎ?

  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.4

気になる点 ・a.txtの数字は、行数ですか?バイト数ですか? fseekに指定するオフセットは「バイト数」です。 「行数」ではありません。 ・a.txtとb.txtの関係は? 双方とも完全に乱数ならば、b.txtの先頭(あるいは、任意のところから)aの行数分だけ取りだしても同じだと思うのですが。 a→bの計算式があるなら、bを読まずに計算するという方法もありますし。 ・C++でやる理由は? この質問にあるだけなら、デバッグと実行時間まで含めてPerlでも使ったほうが早く終わりそうです。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.3

メモリ不足かもしれんしそうではないかもしれん. 単に「動かない」としか書いてくれないので, どちらであるかを判断することは全く不可能だ. しかし, よく見るととてつもなく無駄なプログラムだなぁ. このプログラムからはちょっと離れて, 動作をもっと練った方がいいと思うよ. ところで, b.txt にあるのは小数だと思っていい?

tororo1007
質問者

補足

はい。b.txtは小数です。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

現状だと fseek( fp2, offset2[no2 - 1], SEEK_SET ); で no2 が 100 を超えてたらアウトだってことに気づいてない? でまあ普通は (かつメモリに余裕があれば) #1 の方法が「何も考えなくていい」ので簡単. 逆に a.txt から全部読み込んで, ソートしてから b.txt を読み込むという方針もあります. 今の条件設定ならこっちの方がメモリは少なくてすむ. けどめんどくさいので, よほどメモリが苦しいとき限定で.

tororo1007
質問者

補足

FILE *fp,*fp2,*fp3; char buff[256],buff2[256]; long int offset[5000000],offset2[5000000]; 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 >= 5000000 ){ break; } にすると動かなくなります。 これは、メモリ不足ということなのでしょうか?

  • SaKaKashi
  • ベストアンサー率24% (755/3136)
回答No.1

b.txtを配列に全て読み込んでからa.txtの値を配列の添え字とすればいいのでは。 b.txtは5,000,000件からさらに増加するのですか?

tororo1007
質問者

補足

いえ、b.txtは既知で500万行で、増えません。 b.txtを配列で読み込んでから、a.txtを読み込む方が自然なのでしょうか? 文章がわかりづらかったらすみません。

関連するQ&A

  • c言語  2つのファイルを行ごとに読み込むプログラミング

    c言語  2つのファイルを行ごとに読み込むプログラミング 0.txt と 1.txt という2つのテキストフォルダがあり 0.txt の中身は a a b b 1.txt の中身は c c d d というものとします。 これら2つのフォルダを読み込むとき まず1つのフォルダの1行目(a a)を表示し 他方の1行目(c c) 2行目(d d)を表示させて 続いて1つのフォルダの2行目(b b)を表示し 他方の1行目(c c) 2行目(d d)を表示させたいのです。 つまり実行結果が a a c c a a d d b b  ←理想の実行結果です c c b b d d となるようにしたいのですが #include <stdio.h> #include <stdlib.h> #define STR_MAX 256 int main(void) { FILE *fp, *fp2; int i, j, k; char buf[STR_MAX]; char buf2[STR_MAX]; fp = fopen("0.txt", "r"); fp2 = fopen("1.txt", "r"); if (fp == NULL && fp2 == NULL){ printf("\n"); } while(fgets(buf, STR_MAX, fp) != NULL){ while(fgets(buf2, STR_MAX, fp2) != NULL){ printf("%s%s", buf,buf2); } printf("\n"); } fclose(fp); fclose(fp2); return 0; } このプログラミングの実行結果は a a c c a a d d となり、0.txtの2行目(b b)は表示されません。 おそらく while 文 を2重にすることで 不具合が起きているのだと思うのですが 色々と調べた結果、これ以外に プログラミングが思いつきません。 私の理想の実行結果にするためには どこを訂正させると良いのでしょうか? 恐れ入りますが ご回答 どうかよろしくお願いいたします。

  • C言語のファイル操作についての質問です

    #include <stdio.h> #include<process.h> int main(void) { FILE *fp; int a[200], i, j, cnt, max, max_i; fp = fopen("data.txt", "r"); if (fp == NULL) { printf("file cannot open.\n"); exit(1); } for(i = 0; i < 200 && fscanf(fp, "%d", &a[i]) == 1; ++ i) ; fclose(fp); for(max = max_i = j = 0; j < i; ++ j){ int k; for(cnt = 0, k = j + 1; k < i; ++ k) cnt += (a[j] == a[k]); if(cnt > max) max = cnt; max_i = j; } printf("%d\n", a[max_i]); return 0; } これは「data.txt」というファイルから最頻値を探し出し、その値を表示するプログラムです。 しかし、このプログラムだと最頻値が1つしか表示できないので、 最頻値が複数ある場合でも、すべての最頻値の値を表示させるようなプログラムに書き換えてほしいです。 よろしくお願いします。 例)data.txt 30000 100 150 30000 30000 100 4320 100 出力↓ 30000 100

  • C言語のfork()とpipe()の使用方法についてのサンプルを作成し

    C言語のfork()とpipe()の使用方法についてのサンプルを作成していますが、 期待通りの動作をさせる事ができないため、質問させて頂きます。 現在は、aaa.txtの1行目(てすとだよ。)のみ出力されて2行目が出力されません。 (1)実行方法:a.out aaa.txt (2)期待動作:aaa.txt の内容を出力 <aaa.txt> てすとだよ。 pipeだよ。 <ソース> #include <stdio.h> #include <unistd.h> #include <sys/wait.h> #include <stdlib.h> #include <string.h> #define BUFF_SIZE 1024 /* バッファのサイズ1M */ int main(int argc, char **argv) { int pipefd[2]; int p_id; int ret; /* 関数の戻り値 */ int status; FILE *fp; char buff[BUFF_SIZE]; ret = pipe(pipefd) ; /* パイプ生成失敗 */ if(ret == -1) { perror("main"); exit(EXIT_FAILURE); } /* 子プロセス生成 */ p_id = fork(); /* 子プロ生成失敗 */ if(p_id == -1) { perror("main"); exit(EXIT_FAILURE); } if(p_id == 0) { /* 子プロセス側処理 */ printf("子プロ処理開始!!!\n"); /* 使用しないwrite側はクローズ */ close(pipefd[1]); /* パイプから読込む */ while(read(pipefd[0], &buff, BUFF_SIZE) > 0) { fputs(buff, stdout); } close(pipefd[0]); printf("子プロ処理終了!!!\n"); } else { /* 親プロセス側処理 */ printf("親プロ処理開始!!!\n"); /* 使用しないread側はクローズ */ close(pipefd[0]); printf("親プロ:読込ファイル名[%s]\n",*(argv + 1) ); if( (fp = fopen( *(argv + 1), "r" ) ) != NULL) { while(fgets(buff, BUFF_SIZE, fp) != NULL) { printf("パイプに書込む値:%s\n",&buff[0]); /* パイプに書き込む */ write(pipefd[1], buff, strlen(buff) + 1); } fclose(fp); } else { perror("親プロセス "); } close(pipefd[1]); wait(&status); } return EXIT_SUCCESS; }

  • reallocについて

    現在、領域を拡張しながら、 ファイルを読み込んで呼び元に返却するPGを作成しています。 reallocがうまくいかないので、試しに小さいのを作って みましたが、これだとreallocの2度目で落ちます。 100文字ずつ呼んでいるので、拡張も100文字ずつ行っています。 メモリ確保に失敗なら、まだ分かるのですが、 ちょっと理由がわかりません。 reallocを複数繰り返していることも問題だと思いますが、 まずは正常に処理を流したいと考えています。 よろしくお願いします。 ~~~~~~ソース~~~~~~~~ //ファイルを読み込んでから領域を確保する #include <stdio.h> #include <string.h> #include <stdlib.h> #define BUFF 100 int main() { FILE *fp; char tmp[BUFF+1]; char *str; int len = 0; fp = fopen( "c:/test.txt" , "rb" ); if(fp ==0){ printf("ファイルがありません\n"); return -1; } //領域を初期化 str = (char *)malloc(1); memset(str,'\0',sizeof(str)); while(feof(fp)==0){ memset(tmp,'\0',sizeof(tmp)); fgets(tmp,BUFF,fp); //領域を再確保 len += BUFF+1; if(NULL == ((char *)realloc(str,len))){ printf("メモリ確保エラー"); } //読み込んだ値を変数に追加 strcat(str,tmp); } printf("文字列\n\n%s\n",str); printf("長さ:%d\n",len); fclose(fp); return 0; }

  • プログラムソースの誤りを教えてください

    CSVファイル(カンマでデータの要素が区切られている)の左から数えて15個目と16個目の間のデータを読みとるプログラムをつくりたいのですが、できません。どこがおかしいのか教えてください。 #include<stdio.h> int main(){ FILE *fp; char buff[562],yakusyoku[10]; int youso,n,konma; fp = fopen("jinji.csv","r"); if(fp == NULL) exit(1); fgets(buff,sizeof(buff),fp); n = 0,youso = 0,konma = 0; while (youso < sizeof(buff)){ if(buff[youso] == ','){ konma++; if (konma == 15){//カンマが15個になったら次の処理を行う while(buff[youso] != ','){//次のカンマ(16個目のカンマに会うまで次の処理を行う) yakusyoku[n] = buff[youso]; youso++,n++; break; } } } youso++; } fclose(fp); }

  • C言語 ファイル内のデータと入力したデータの重複

    テキストファイルを読み込み、入力したデータとの重複がないかどうかを調べたいのですが、 わからない点があるため、質問させていただきます。 -------------------------------------------------------- #include <stdio.h> #include <stdlib.h> #include <string.h> int main() {    FILE *fp;    char datafile[];= "sample.txt";    char buff[512]; //読み込んだ1行分のデータを格納    char *data[1000]; //読み込んだデータを格納    int data_c = 0; //データの数    char str[256]; //入力された文字列を格納    int i;    int check; //重複チェック         (中略)    //ファイルを1行ずつ読み込み、その長さのメモリを確保し、値をコピー    while(fgets(buff, sizeof buff, fp) != NULL) {      data[data_c] = (char*)malloc(strlen(buff) + 1);      strcpy(data[data_c++], buff);    }         (中略)    //文字列を入力    fgets(str, 256, stdin);    check = 0;    //すでにあるデータと入力したデータの重複を調べる    for(i=0; i<data_c; i++) {      if(strcmp(data[i], str) == 0) {      check = 1;      break;      }    }         (中略) -------------------------------------------------------- 例えば読み込むファイルに5行書かれていた場合、 data[0]からdata[4]に確保したメモリの先頭アドレスが格納されますよね? ということはdata_cの値は4となるのですが、 その後のファイルデータと入力したデータの重複を調べるところで、 for(i=0; i<data_c; i++) となっており、data[0]からdata[3]までの4行分しか調べられないことになります。 なぜ、i<=data_cではなく、i<data_cとなっているのか、わかりましたら教えていただけますでしょうか。

  • tab

    下のプログラムソースは、jinji.txtの各行の一列目を読み取るプログラムです。 jinji.txtには項目がtabで区切られています。 たとえば、下のようになっています。 番号 tab 姓 tab 名 1 tab 高田 tab 太郎 2 tab 山田 tab 順平 … 300 tab 永井 tab 晃 ソースの真ん中ぐらいに、 while(buff[count]!=' ') とあります。これは最初のtabまで読み取る、つまり一列目を読み取るコードなのですが、tabは「' '」でいいのでしょうか?「' '」でtabだと判断できますか? #include<stdio.h> #define num 100 int main(){ FILE *fp; char buff[num],bango[10]; int count; fp = fopen("jinji.txt","r"); if(fp == NULL) exit(1); fgets(buff,sizeof(buff),fp); count=0; while(buff[count]!=' '){ bango[count]=buff[count]; count++; } //printf("%s\n",bango); fclose(fp); }

  • 最大値(c++)

    3つの数の最大値をArrayを使って求めるProgramを書いてます。何とかそれっぽくできたのですが、実行すると、一番初めに入れた数が最大値であると、表示されてしまいます。 なにが問題なのでしょうか? #include<iostream.h> int max(int n[], int size){ int max= n[0]; for (int i=1; i>size; i++) if(n[i]>max) max= n[i]; return max; } int main(){ int a,b,c; cout<<" 3つの数の最大値を求めます。順に3つの数字をいれてください。"<<endl; cin>>a>>b>>c; int d[]={ a,b,c }; cout<<"max :"<<max(d,sizeof d/ sizeof d[0])<<endl; return 0; }

  • ファイルの読み込みとメモリ確保について。

    ファイルから文字を読み込んで それを配列に入れて辞書順にソートさせようとしています。 それで、ソート以前の問題なのですが、ファイルから文字列を読み込んで配列にいれようとするのですが、 buffを動的にメモリ確保してその配列に入れたいと考えているのですが、なぜか入ってくれません。 whileでファイルの終わりがくるまで一行ずつ読み込んで それをsに入れていき、sをbuff[]の配列に順番にいれていこうとしているのですが・・・。 ファイルは aaaa aabc dda wer zie ced sdfe be など適当な文字の並びです。 malloc関数で動的に確保したメモリはその後普通の配列と同様に使えるのではなかったのでしょうか? なので普通にbuff[i]=s;といった処理で入れれると思ったのですが。 ファイルは一行の長さの最大が100で 行数が4000行あると仮定しています。 今は小さいファイルでテストしていますが。 以下ソースです。 #include <stdio.h> #include <stdlib.h> #define MAX_SIZE 100 #define MAX_LINE 4000 main() { FILE *fp; char *buff,s[MAX_SIZE]; int i; fp=fopen("words.txt","r"); buff=(char*)malloc(sizeof(char)*MAX_LINE); i=0; while(fgets(s,MAX_SIZE-1,fp)!=NULL){ buff[i]=s; printf("%s",buff[i]); i++; } fclose(fp); } とりあえずファイルの内容を配列に入れないとソートできないので、配列に全て入れてしまいたいと考えています。 間違いがどこにあるのか指摘よろしくおねがいします。m(-_-)m

  • C言語 複数ファイル操作について

    Cプログラミング初心者です。 論文などの何行も文章があるようなテキストファイル(ここでは1.txtとします)と、他に予め単語をいくつか登録しているテキストファイル(2.txt)を開き、1.txtを最初の行から一行ごとに読み込み、2.txtの中にある単語が1つでもその一行の文章中に含まれていたらその一行の文章を出力し、また次の行においても2.txtの中にある単語のいずれかが含まれているかどうかを調べて含まれている場合は出力…含まれていない場合は出力せずに次の行へ…といったようにこれを1.txt内の最後の行まで繰り返し行うプログラムを作りたいのですが、自分が作ったプログラムでは含む・含まない関係なく1.txt内の文章全てが出力されてしまいます。おそらく最初のwhile文あたりがおかしいのだろうという予想はつくのですがどのように直せばよいのかわからず悩んでいます。どなたか教えていただければ嬉しいです(;_:) #include <stdio.h> #include <stdlib.h> #include <string.h> #define N 1056 void delkaigyo(char *s1,char *s2){ char *p = s1; p=strstr(s1,s2); if(p!=NULL){ strcpy(p,p+strlen(s2)); delkaigyo(p+1,s2); } } int main(void){ FILE *fp; char *filename = "2.txt"; char str1[N]; char str2[N]; char kaigyo[] = "\n"; int i; int a=0; char fname[64]; printf("file:"); scanf("%s", fname);   ←ここで1.txtを入力するとします fp = fopen(fname, "r"); while(fgets(str1, N, fp) != NULL){ delkaigyo(str1, kaigyo); memset(str1, 0, N); fread(str1, 1, N-1, fp); if((fp = fopen(filename, "r")) == NULL){ fprintf(stderr, "%serror.\n", filename); exit(EXIT_FAILURE); } while(fgets(str2, N, fp) != NULL){ delkaigyo(str2,kaigyo); if(strstr(str1,str2)!=NULL){ a = 1; printf("%s\n", str1); break; } } if(a==0){ return 0; } fclose(fp); } return EXIT_SUCCESS; }

専門家に質問してみよう