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);
}
}
A 回答 (7件)
- 最新から表示
- 回答順に表示
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のコピーくらいの時間ですが)
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> にすれば, もっと短くなる.
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]);"
のワンライナーですしね... って, さすがにこれはやりすぎ?
No.4
- 回答日時:
気になる点
・a.txtの数字は、行数ですか?バイト数ですか?
fseekに指定するオフセットは「バイト数」です。
「行数」ではありません。
・a.txtとb.txtの関係は?
双方とも完全に乱数ならば、b.txtの先頭(あるいは、任意のところから)aの行数分だけ取りだしても同じだと思うのですが。
a→bの計算式があるなら、bを読まずに計算するという方法もありますし。
・C++でやる理由は?
この質問にあるだけなら、デバッグと実行時間まで含めてPerlでも使ったほうが早く終わりそうです。
No.2
- 回答日時:
現状だと
fseek( fp2, offset2[no2 - 1], SEEK_SET );
で no2 が 100 を超えてたらアウトだってことに気づいてない?
でまあ普通は (かつメモリに余裕があれば) #1 の方法が「何も考えなくていい」ので簡単.
逆に a.txt から全部読み込んで, ソートしてから b.txt を読み込むという方針もあります. 今の条件設定ならこっちの方がメモリは少なくてすむ. けどめんどくさいので, よほどメモリが苦しいとき限定で.
この回答への補足
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;
}
にすると動かなくなります。
これは、メモリ不足ということなのでしょうか?
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・人生のプチ美学を教えてください!!
- ・10秒目をつむったら…
- ・あなたの習慣について教えてください!!
- ・牛、豚、鶏、どれか一つ食べられなくなるとしたら?
- ・【大喜利】【投稿~9/18】 おとぎ話『桃太郎』の知られざるエピソード
- ・街中で見かけて「グッときた人」の思い出
- ・「一気に最後まで読んだ」本、教えて下さい!
- ・幼稚園時代「何組」でしたか?
- ・激凹みから立ち直る方法
- ・1つだけ過去を変えられるとしたら?
- ・【あるあるbot連動企画】あるあるbotに投稿したけど採用されなかったあるある募集
- ・【あるあるbot連動企画】フォロワー20万人のアカウントであなたのあるあるを披露してみませんか?
- ・映画のエンドロール観る派?観ない派?
- ・海外旅行から帰ってきたら、まず何を食べる?
- ・誕生日にもらった意外なもの
- ・天使と悪魔選手権
- ・ちょっと先の未来クイズ第2問
- ・【大喜利】【投稿~9/7】 ロボットの住む世界で流行ってる罰ゲームとは?
- ・推しミネラルウォーターはありますか?
- ・都道府県穴埋めゲーム
- ・この人頭いいなと思ったエピソード
- ・準・究極の選択
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
複数ファイルの同時読み込みの...
-
C言語 csv 配列
-
日本語ファイル名のFTPについて
-
fopenでファイル名に、変数を使...
-
大量の入力ファイルを扱うとき...
-
テキストファイル内に対して, ...
-
C言語でセグメンテーションエ...
-
C言語のファイル入出力の問題です
-
C言語でファイル読み書きを早く...
-
C言語 BUF_SIZE文字以上ある行...
-
ADボードのデータの遅れについて
-
c言語でのfscanfについて
-
write関数でファイルディスクリ...
-
ファイル結合
-
テキストファイルをバイナリフ...
-
行毎の黒のドット(ピクセル)...
-
ファイルが読み込めない・・・
-
C言語 初心者です。
-
c言語による画像処理について
-
エラー C2664
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
c言語でのfscanfについて
-
ファイルへの書込み処理が異常...
-
C言語でファイル読み書きを早く...
-
エラーがわかりません、、
-
日本語ファイル名のFTPについて
-
fopenでファイル名に、変数を使...
-
ファイル出力で改行を入れたい!
-
テキストファイル内に対して, ...
-
複数ファイルの同時読み込みの...
-
C言語の課題です
-
C言語を用いて、csvファイル内...
-
バイナリファイルの読み込み(C...
-
C言語にてXMLファイルから任意...
-
ファイルが読み込めない・・・
-
構造体のメンバにファイルポイ...
-
CRC32の計算方法
-
ファイルからCR/LFを除去したい
-
C言語におけるファイル読み込み...
-
C言語 csv 配列
-
エラー C2664
おすすめ情報