- ベストアンサー
C言語 ファイルの指定された行を表示
こんにちは。 回答お願いします。 今私は作業の高効率化を目指すためプログラムを考えています。 まだぜんぜんできていませんが・・ ファイルの指定された行を表示する関数がないだろうか? もしくは似たような方法はないだろうかと考えています。 できれば例題とともに教えていただければ幸いです。 具体的にどういう風にしたいのかというと ----test.txt------- aaaa bbbbb cccccc dddd eeeeeeee ffffff ------------------- というファイルがあったとしたらgetsで4と入れてやったら 四行目のddddが表示されるようにしたいのです。 まだまだ初心者ですのでさっと考えることができません。 どうかご教授お願いします。
- みんなの回答 (3)
- 専門家の回答
質問者が選んだベストアンサー
★高効率を目指しているの? ・固定長データなら高効率で1行を取得できたりします。 例えば ----test.txt------- aaaaa bbbbb ccccc ddddd eeeee fffff ------------------- という固定長データ(5文字×6行)の場合は int no = 4; ←4行目を取得したい時 fseek( fp, ((no - 1) * 7), SEEK_SET ); ←5文字+\r+\n=『7』 fgets( buff, sizeof(buff), fp ); ↑ これなら行番号で指定した1行を fgets() 関数で取得可能です。 ※なおバイナリモードでオープンして下さい。 ・可変長データの場合は行の先頭のオフセット位置を最初の読み込みで管理します。 例えば ----test.txt------- aaaa bbbbb cccccc dddd eeeeeeee ffffff ------------------- という可変長データ(4,5,6,4,8,6文字)の場合は オフセット位置の配列を行数分用意します。→事前に分かれば楽ですね。行数。 long offset[ 100 ]; ←100行だと仮定 int max; for ( max = 0 ; !feof(fp) ; max++ ){ if ( max >= 100 ){ ←安全対策 break; } offset[ max ] = ftell( fp ); fgets( buff, sizeof(buff), fp ); } ↑ ここまでがオフセット位置の読み込みです。次は読み出しです。 int no = 4; ←4行目を取得したい時 fseek( fp, offset[no - 1], SEEK_SET ); fgets( buff, sizeof(buff), fp ); ↑ これで行番号で指定した1行を fgets() 関数で取得可能です。 ※やっぱりバイナリモードでオープンして下さい。 ・あと行数の指定時に 1~max の範囲になるように補正処理も入れたほうが良いかも。 例えば if ( no < 1 ){ no = 1; } else if ( no >= max ){ no = max; } ↑ こんな感じで。 ・以上を参考にして下さい。 下の『参考URL』もどうぞ。
その他の回答 (2)
- Oh-Orange
- ベストアンサー率63% (854/1345)
★回答者 No.1 です。行数を最初に数えるの? >行数が不明な場合 ↑ 最初に行数を数えると巨大な(行数が多い)ファイルだと初期化に時間がかかります。 でも一度、読み込んでしまえば後は行数指定で行を取得できますね。 >long offset[ count ]; >って感じで宣言できませんかね・・ ↑ C99 に対応していれば可能です。 既に回答者 No.2 さんのアドバイスにあるとおりです。 余談: ・この質問はどんな処理に利用するのでしょうか? 昔、MS-DOS 時代の時にページャソフト(テキスト・ビューワ)を作ったときに同じ考えで 行の先頭オフセット位置を管理して表示、ジャンプさせました。 あの時代はメモリが少なく、また実行時のメモリを抑えるためにスモールモデルや タイニーモデルで作りました。 ・タイニーモデルでは全体で 64KB に制限されるためオフセット位置のメモリ(配列)を 工夫しました。具体的には 100行単位でのオフセットを記憶・管理します。 その後 123 行から表示をする場合は 100行目の位置へ移動してそこから 199行までの 行の先頭オフセット位置を読み込みます。そして 123 行からの表示をさせました。 ・こんな工夫のお陰か巨大な(行数の多い)ファイルでも一度オフセット位置を読み込めば 任意の位置からジャンプして表示するのにあまり時間がかかりませんでした。 でも決して高速表示ではありませんよ。 メモリを節約して行数の多いファイルも閲覧出来ることを目的としていますので。 最大 100,000 行まで覗けました。100行単位に分割しているのがミソです。 動的確保: ・C99 に対応していないと動的確保になりますが、このときに行とオフセット位置の 読み込みを同時にした方が良いでしょう。もちろん最初に行数を単純にカウントしてから 動的に確保しても良いですが。その方が簡単ですしね。 ・でも同時にする場合は1024行単位でオフセット位置配列をリスト構造で用意します。 つまり、 typedef struct offset_t { long offset[ 1024 ]; struct offset_t *next; } offset_t; という構造体を用意して static struct { struct offset_t *head; // 単方向リストの先頭 struct offset_t *tail; // 単方向リストの最後 struct offset_t **table; // 1024行単位の配列(これが参照用テーブル) int maxBlock; // 確保したブロック数 int maxCount; // 読み込んだ最大行数 } sys; という構造体で管理します。 ・上記の構造体を使って最初 1024 行まで読み込みます。同時にオフセット位置もセット。 そして 1024 行を超える場合は新しく struct offset_t 構造体を動的確保して鎖のように つなげていきます。これをファイルの最後まで 1024 行単位で繰り返します。 ・すべてオフセット位置を読み込んだ後は maxCount / 1024 のブロック数のポインタ配列を 動的に確保します。→struct offset_t 型の配列 確保したらそのポインタ配列 table[0]~table[maxBlock - 1] に単方向リストの先頭 head から順に struct offset_t 型のポインタをセットしていきます。 これで table[0]…0~1023行のオフセット table[1]…1024~2047行のオフセット table[2]…2048~3071行のオフセット : という感じになります。 ※maxBlock は (maxCount / 1024) + !!(maxCount % 1024) という数です。 ※maxBlock は (maxCount >> 10) + !!(maxCount & 0x3FF) と計算しても良い。 ・指定行のジャンプ・取得では int no = 12345; ←12345行目を取得したい時 no--; ←行数を 0~??? にするためデクリメント fseek( fp, sys.table[no >> 10].offset[no & 0x3FF], SEEK_SET ); fgets( buff, sizeof(buff), fp ); という感じで取得できます。 ※1024 行単位にしている理由はビットシフト、ビットAND でアクセスするためです。 ・あと一度 table を確保したらそのポインタ配列(ブロック数)を maxBlock で管理して 別ファイルを再読み込みする場合に maxBlock 以下ならそのまま、maxBlock 以上になったら 拡張するため table を realloc または free/malloc して下さい。 その他、C++ の Vector などで管理するのも良い。C 言語ならリストで実装するなど工夫。 ・以上。参考に。
お礼
大変わかりやすく記述されてありがとうございます。 私にはちょっと難しい感じもしますがチャレンジしてみます。
- aris-wiz
- ベストアンサー率38% (96/252)
>long offset[ count ]; gccかC99規格の標準コンパイラなら可能です。 それ以外の環境でするなら、動的なメモリ確保を行うことになります。
お礼
回答ありがとうございます。 2003.netは対応してなかったみたいです。。
お礼
回答ありがとうございます。 よくわかりました。 行数が不明な場合 while(fgets(buff, STRING_MAX, filep) != NULL){ /* 1行読み込み */ count++; /* 行数カウント */ } 見たいな感じでカウントしてやって long offset[ count ]; って感じで宣言できませんかね・・ 手元にコンパイラ入ったPCがないもので・・ 今日戻ったら試してみようと思います。