• ベストアンサー

ファイルの読み取りについて

現在C言語の勉強中ですが、 テキストファイルがあったとします。 ファイル内容 abc ,111 def ,222 ghi ,333 っとあったとします。 プログラムで入力した数値で、どの行番号のどのブロックの 文字列を取得し表示したいのですが、行とブロックの指定方法が わからなくて困っています。 ご回答よろしくお願いします。

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

  • ベストアンサー
  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.4

★もう一つアドバイスします。 ・回答者 No.2 さんの補足で実行速度を削りたくないようならば、1行のデータを  固定長のデータとします。つまり、『x,y,w,h』を2桁(3桁)のカンマ区切りのデータ  とするのです。 データ例: //x, y , w , h ←実際には記述しない(記述すると計算式を調整する必要がある) 000,000,050,050 000,000,050,050 000,000,050,050 000,000,050,050    : 高速に: ・上記のデータ例のように固定長のデータにすれば、次の計算式でランダムアクセスを行えます。  計算式⇒『(行番号×17)+(列番号×4)』⇒ランダムアクセスするオフセット位置になる。 ・上の計算式の『行番号』、『列番号』ともに『0』から始まる数値です。  つまり、1段目のデータ行が『行番号』で『0』、2段目のデータ行が『1』を表す。  同様に1列目のカンマ数値が『列番号』で『0』、2列目のカンマ数値が『1』を表す。 ・オフセット位置が計算できたらば『fread』関数で 17 バイトを読み込む。  char buff[ 32 ];  int x, y, w, h;    fseek( fp, ((行) * 17L + (列) * 4L), SEEK_SET ); ←オフセット位置に移動    if ( fread(buff,sizeof(char),sizeof(buff),fp) == sizeof(buff) ){   x = atoi( buff + 0 * 4 ); ←座標を数値に変換   y = atoi( buff + 1 * 4 );   w = atoi( buff + 2 * 4 );   h = atoi( buff + 3 * 4 );  }  else{   エラー処理  } 最後に: ・上記の計算式の 17 とは1行データの『000,000,050,050』が 15 バイト+『\r』『\n』の 2 バイトで  17 バイトとなるのです。カンマ区切りの1データの桁数を変更するときは計算式も変更して下さい。  たとえば、上記ではすべて 3 桁で記述するようにしていますが、4 桁で記述するように変更すると  計算式は『(行) * 21L + (列) * 5L』となります。→分かりますよね。 ・以上。おわり。→データファイルのオープンは『バイナリ形式』にしましょう。

koedame
質問者

補足

ご回答感謝します。 私が求めていた答えだと思います。ありがとうございます。 最後にどうしてバイナリ形式のファイルオープンなのか知りたいです。 普通のテキスト形式では駄目なのでしょうか?

その他の回答 (4)

  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.5

★改行コードの扱いでバイナリの方が良いと薦めたのです。 ・『fseek』関数を『SEEK_SET』定数で使っているので今回の場合は、本当は  テキストモードでもバイナリモードでも両方とも正しく動きます。 ・それで『fseek』関数に『SEEK_CUR』定数や『SEEK_END』定数を使ったとき、  テキストモードだと『\r\n』が『\n』の1つの変換されて『fgets』などでは  返されますよね。この返された文字列長を単純に『strlen』で加算してオフセット値  を管理していると『SEEK_CUR』定数や『SEEK_END』定数のときにバイト数が  ずれるのです。このことから、バイナリモードにすれば『\r\n』はそのまま  2バイトで管理されますのでずれないという理由からです。 ・今後、テキストモードで『fseek』関数を『SEEK_CUR』や『SEEK_END』定数で  ファイルポインタを移動するときは注意して下さい。 ・以上。おわり。→結論、今回はどちらのモードでも良いです。

koedame
質問者

お礼

なるほど、何度もありがとうございます。 とても勉強になりました。 本当にありがとうございます。

  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.3

★ブロックとはカンマで区切られたデータのことですか? ・プログラムで入力した数値がデータの数値ならば、1行ずつ読み込んでカンマ区切りの  データと比較します。そして、見つかった行を表示しますが、行番号も一緒に表示するには  1行ずつ読み込むときにカウンタで行数を同時に数えておけば良いでしょう。 ・カンマ区切りのデータは『strchr』や『strpbrk』でカンマ文字を検索して見つかった位置に  NULL 文字をセットします。これで1つ1つのデータを分割できます。あと『strtok』関数でも  分割できます。 ・下にカンマ区切りのデータを分割するサンプルを載せておきます。 サンプル: char buff[ 256 ]; char *token[ 100 ]; ←カンマ区切りのデータ(ポインタへの配列) char *find; char **seek; while ( fgets(buff,sizeof(buff),fp) != NULL ){ ←『fp』がファイルポインタ  seek = token;  *seek++ = find = buff;    while ( (find = strchr(find,',')) != NULL ){   *find++ = '\0';   *seek++ = find;  }  *seek = NULL;  /*  『token』の[0]~[n]にカンマ区切りのデータが分割セットされる  ここで入力された数値などと比較を行います。  token[0] 1つ目のデータ  token[1] 2つ目のデータ  token[2] 3つ目のデータ   :  token[n] 最後のデータ  token[n+1] NULL  */ } 最後に: ・『ブロック』の意味やどんな文字列を取得して表示したいのですか?  イマイチよく分かりませんが、行単位でデータブロックを読み込んで数値文字列と比較などを行って下さい。 ・以上。おわり。

koedame
質問者

お礼

補足に関しては先にご回答いただいた方と同じです。 『strchr』や『strpbrk』に関しては全然知りませんでしたし、 私が考えているプログラムにおいて恐らく使えそうです。 感謝します。_(__)_

  • noro6677
  • ベストアンサー率21% (34/158)
回答No.2

行に関してはループで行数をカウントすれば良いだけだし ブロックとは? abc ,111とあった場合のabcと111を別々のブロックと言うこと? だったら簡単なのは「,」で文字列を分解すること。

koedame
質問者

補足

ご回答ありがとうございます。 実は私はゲームプログラムを勉強しているものですが、 画像ファイルのアイコンの位置情報を テキストファイルにスクリプトとして組もうとしているのですが、 ファイル内容としては以下のようになります。 // x,y, w, h   0,0,50,50   0,0,50,50   0,0,50,50   0,0,50,50     ・     ・     ・ x,y は画像ファイル内での左上の位置 w,h はそのアイコンの幅と高さです。 ブロックとは言い換えれば列です。(すみません、なんでブロックって記述してしまったのか(f^^)) x,y,w,h がブロックに当たります。 他のテキストファイルから読み込んだ値を使って、 つまり行番号を取得して、 その取得した行番号を使ってダイレクトにその行のデータを 取得し、その行のアイコン情報をブロックごと取得し、 他の変数に格納したいわけです。 for文など使い実行速度を削りたくないのです。 すみません長くなって。

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.1

質問の内容があやふやで答えるのが難しいので補足をお願いします。 「プログラムで入力された数値」とはファイルの行番号ということでよいのでしょうか? だとして、「ブロック」というのは何を指して云っているのですか? 「行とブロックの指定方法がわからない」ということですが、 たとえばWindowsプログラムのように入力のダイアログボックスを開きたいとか、 コマンドプロンプトから起動していて、 行番号を入力してください > のようなメッセージを出して入力待ちにすることがわからないということでしょうか? ここでも「ブロック」が登場していますが、具体的にどういうものが ブロックなのかを明示してください。

koedame
質問者

補足

実際は下のように入力を待って取得した二つの値から行とブロックを 指定するのです。 main() {   int nBox[2];   scanf( "%d", &nBox[ 0 ] );//行   scanf( "%d", &nBox[ 1 ] );//ブロック /* この辺でその取得した値を用いて データを表示したいのです。 */   return 0; } 下の例だと縦が行で横がブロックです。 abc ,111 def ,222 ghi ,333 最後になりましたが質問の内容が足りなかったことにお詫びします。