教えて!gooにおける不適切な投稿への対応について

こんにちは。
回答お願いします。
今私は作業の高効率化を目指すためプログラムを考えています。
まだぜんぜんできていませんが・・
ファイルの指定された行を表示する関数がないだろうか?
もしくは似たような方法はないだろうかと考えています。

できれば例題とともに教えていただければ幸いです。
具体的にどういう風にしたいのかというと
----test.txt-------
aaaa
bbbbb
cccccc
dddd
eeeeeeee
ffffff
-------------------
というファイルがあったとしたらgetsで4と入れてやったら
四行目のddddが表示されるようにしたいのです。
まだまだ初心者ですのでさっと考えることができません。
どうかご教授お願いします。

gooドクター

A 回答 (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』もどうぞ。

参考URL:http://www9.plala.or.jp/sgwr-t/lib/fseek.html
    • good
    • 0
この回答へのお礼

回答ありがとうございます。
よくわかりました。
行数が不明な場合
while(fgets(buff, STRING_MAX, filep) != NULL){ /* 1行読み込み */
count++; /* 行数カウント */
}
見たいな感じでカウントしてやって
long offset[ count ];
って感じで宣言できませんかね・・
手元にコンパイラ入ったPCがないもので・・
今日戻ったら試してみようと思います。

お礼日時:2007/07/11 07:41

★回答者 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 言語ならリストで実装するなど工夫。
・以上。参考に。
    • good
    • 0
この回答へのお礼

大変わかりやすく記述されてありがとうございます。
私にはちょっと難しい感じもしますがチャレンジしてみます。

お礼日時:2007/07/12 16:05

>long offset[ count ];


gccかC99規格の標準コンパイラなら可能です。
それ以外の環境でするなら、動的なメモリ確保を行うことになります。
    • good
    • 0
この回答へのお礼

回答ありがとうございます。
2003.netは対応してなかったみたいです。。

お礼日時:2007/07/12 16:03

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています

gooドクター

このQ&Aを見た人がよく見るQ&A

人気Q&Aランキング