- ベストアンサー
テーブル(配列)の初期化を外部ファイルに指定
お世話になっております。 以下は列車の時刻表を検索するCのソースです // ヘッダー省略 #define PRN_PORTD 0x0378 #define PRN_PORTC 0x037a void lightLED( int num, int loc ) { static int table[] = { 0x3F, // 0 0x06, // 1 0x5B, // 2 0x4F, // 3 0x66, // 4 0x6D, // 5 0x7D, // 6 0x27, // 7 0x7F, // 8 0x6F, // 9 }; // table[ num ] で数値から変換 printf("num[%x] port[%x]\n loc[%d]", able[ num ], PRN_PORTD, loc ); // デバックの為 outb( table[ num ], PRN_PORTD ); outb( loc, PRN_PORTC ); outb( loc | 0x04, PRN_PORTC ); outb( loc, PRN_PORTC ); } // 時刻表の時:分を表示する関数 void digitalLED( int time ) { int ret, four, three, two, one; four = ((time / 1000) % 10); // 4桁目を取り出す three = ((time / 100) % 10); // 3桁目を取り出す two = ((time / 10) % 10); // 2桁目を取り出す one = ((time / 1) % 10); // 1桁目を取り出す ret = ioperm( PRN_PORTD, 4, 1 ); lightLED( four, 0x03 ); // 時の10桁を表示 lightLED( three, 0x02 ); // 時の 1桁を表示 lightLED( two, 0x01 ); // 分の10桁を表示 lightLED( one, 0x00 ); // 分の 1桁を表示 ret = ioperm( PRN_PORTD, 4, 0 ); exit(0); } // ここまでのソースはLEDに表示させる関数です int main(void) { char type; int ret; do { // ↓テーブル初期化。本来はこれを使用したいのですが。 // static int table[] = { ここで外部ファイル指定 }; / int findtime; // 0000形式に変換後の現在時間 int *search; // テーブル内の検索時間 int h; // 時 int m; // 分 FILE *file; time_t timer; // 現在時刻を取得し地方形式に struct tm *local; time(&timer); // 現在時刻の取得 local = localtime(&timer); // 現在時刻を構造体に変換 h = local->tm_hour; // 時 m = local->tm_min; // 分 findtime = h * 100 + m ; // 現在時間を0000形式へ変換 file = fopen( "jikoku.txt", "r" ); if( file == NULL ) { puts( "jikoku.txtが開けません" ); return 1; } while ( !feof(file)) { fscanf(file, "%d", &*search); printf("%d\n", *search); // 該当時間検索 // for ( search = table ; *search != -1 ; search++ ) { // ↑時刻表を外部ファイルにしたことによって使わなく // なったのですが。。。 if ( *search > findtime ){ break; } } // 結果表示 if ( *search == - 1 ) { // テーブル内の-1に当たったら //06:10表示 // ↓table[0]の形に要修正 printf( "06:10\n\n" ); digitalLED( 610 ); } else { digitalLED( *search ); printf( "%02d:%02d\n\n", (*search / 100), // 「時」取り出し (*search % 100) ); // 「分」取り出し } fclose(file); scanf("%c", &type); // Enterで再度プログラム開始 } while (type == '\n'); } テーブルの外部ファイルは以下の様な感じです 610 650 ..... 2310 2350 -1 まだ勉強中の初心者です。汚いソースで申し訳ないです。 これでもみなさんに強力して頂きながら数日かけて、やっとここまでできました。 【困っている箇所】 ・この状態ですと、現在時間の「時」が2桁(10~23時)の場合、セグメン テーションエラーですとの表示で実行できなくなります。 ・コメントになってしまっているテーブル初期化を外部ファイル (jikoku.txt)にしたいのですが(teble[0]→6:10 teble[1]→ 6:50 ...の様に。)ファイル名を入れたり色々試したのですがなかなかで きません ・テーブル内で-1に当たったら、6:10分を表示させる箇所も、テーブル使い たいのですが、これも自分でコーディングするとコンパイルが通りませ ん。 長々と申し訳ありません。ご教授願います。 --前回ご覧頂いた方-- 結局期限を明日までにのばしてもらいました。。
- みんなの回答 (6)
- 専門家の回答
質問者が選んだベストアンサー
★最初に >昨日の補足に記載したソースで、会社では動いていたと思うのですが。。。 >処理系によって変わるのでしょうか。 ↑のことから会社では Microsoft Visual C/C++ で自宅では Borland C/C++ の コンパイラを使っているものと推測します。 理由は scanf() 関数の引数 type の型が char 型で宣言されています。 本当は int 型でないといけないのに初心者が良く間違う場所です。 そして、VC は初期設定で構造体などの変数のパディングが 4 バイトとなって います。でも、BCC は初期設定で 1 バイトにメモリのアライメントが生のままです。 ・よって、自宅の BCC ではメモリを破壊しています。 VC では運良く動作したに過ぎないのです。 ・下に main() 関数の分かりやすいソースを載せます。これを元に全面的にソース内容を 見直して書き換えてみて下さい。今まで頑張って作ってきていますが、なるべく main() は スッキリと見やすくして、サブ関数を利用して記述することを今後、心がけて下さい。 サンプル: int getfindtime( void ); ←サブ関数のプロトタイプ宣言です。 int main( void ) { int type; ←scanf()用。型指定はchar型ではない。注意! do { int table[ TABLE_SIZE ]; // 時刻表のテーブル設定 int findtime; // 0000形式に変換後の現在時間 int temptime; // fscanf() 用のint変数 int pos; // table[] の現在位置 int *search; // テーブル内の検索時間 FILE *fp; ←ファイルポインタは fp の方が習慣的に分かりやすい。 // 外部ファイルから table[] に時刻表をセット if ( (fp = fopen("jikoku.txt","r")) == NULL ){ puts( "jikoku.txtが開けません" ); return 1; } for ( pos = 0 ; fscanf(fp,"%d",&temptime) == 1 ; pos++ ){ if ( pos >= TABLE_SIZE ){ puts( "これ以上 table[] 領域に時刻表をセットできません。強制終了します。" ); return 2; } table[ pos ] = temptime; } fclose( fp ); // ここで最新の時刻表を検索 findtime = getfindtime(); ←このサブ関数は自分で作る for ( search = table ; *search != -1 ; search++ ){ if ( *search > findtime ){ break; } } // 検索した時刻表を表示 if ( *search == -1 ){ digitalLED( search[0] ); } else{ digitalLED( *search ); } // Enter押下後再度プログラム開始 scanf( "%c", &type ); } while ( type == '\n' ); return 0; } // 現在の時刻を0000形式で返す関数 int getfindtime( void ) { int h; // 時 int m; // 分 time_t timer; // 現在時刻を取得し地方形式に struct tm *local; /* ここは自分で記述してみましょう。 main() に書かれていた現在時刻の取得と0000形式への変換部分です。 */ return 0000形式の時刻を戻す; } 解説: ・コメントや解説矢印を一杯つけましたのでサンプルの main()、getfindtime() のサブ関数の 使われ方などを全体的に読み取って下さい。特に外部ファイルからデータを読み込む部分を 注意してみて下さい。if ( pos >= TABLE_SIZE ){ … } というエラー処理が入っています。 これの記述を入れないとデータ項目が多き場合は実行時のエラーになります。 table[] は TABLE_SIZE=40 との事ですが、40 以上のデータが外部ファイルに記述されたときの 安全対策として絶対に入れておきましょう。また、外部ファイルの形式は0000形式で最後に必ず -1 を記述することが絶対条件です。必ず最後に -1 を記述すること。 ・最新の時刻表の検索は最初の search ポインタによる検索に戻しました。 >せっかくテーブルがあるのに何でつかわないのかと言う忠告を受けまして。 ↑という事なのでこれに合わせました。 ・その他いろいろと載せたサンプルと自分で作ってきたソースとの比較をしてコーディング方法などを お勉強して下さい。特に初心者さんは main() 関数のみで作成してしまいがちですので。たまには サブ関数に分けて処理を分かりやすくする方法を常に考えならプログラミングします。今回は現在の 時刻を取得して0000形式に変換した値を返すサブ関数 getfindtime() を作ってみました。 他にも外部ファイルから table[] にデータをセットする関数や、最新の時刻表の検索ルーチンを サブ関数にするともっとスッキリと分かりやすくなると思います。時間があったらソースを書き換えて 見ましょう。 ・あと外部ファイルからデータを table[] に入れるので static はつけなくても良いかもしれない。 もちろん、つけても問題はありませんがね。→自由かな。今回は。 ・以上。今後の参考に。
その他の回答 (5)
- aris-wiz
- ベストアンサー率38% (96/252)
No.4です 確認ですが、困っている箇所は ・この状態ですと、現在時間の「時」が2桁(10~23時)の場合セグメン テーションエラー ・コメントになってしまっているテーブル初期化を外部ファイル ・テーブル内で-1に当たったら、6:10分を表示させる箇所 なのですよね? 質問内容が途中から内容がかわってたりしませんか? そうであるならば、時間に55時なんて無いわけですし、 ハードウェアにかなり依存するプログラムです。 そういった制約があるのかなどの仕様が全く分からないので、 LED部分に関して言えることは少ないです。 PRN_PORTD がどういう操作をする値なのかなどの 説明をお願いします。
お礼
確かに質問内容が少し変わっています。 初めて見た方には分かりづらい質問ですね。 申し訳ないです。 Oh-Orangeさんからの回答も元にコーディングします。 見直すきっかけを与えて頂いたことに感謝します。 ありがとうございます。
- aris-wiz
- ベストアンサー率38% (96/252)
ポインタは可読性がなくなるので、 分かりにくいと思ったらコードを短くするために 関数に分けるのも手です。 今回は典型的なメモリ破壊です。 順を追っていきましょう。 まず、searchはint型へのポインタです。 しかしsearchには、手紙で言うあて先が ありません。 データを入れる領域が無いにもかかわらず、 元々入っていた(どこだか分からない)あて先に 値を入れようとしているためセグメンテーションエラー が起こります。(動いた時はたまたま動いてただけ) 補足回答でjtimeをファイル読み込みのwhileの中で インクリメントしていますが、ファイルの内容のデータが TABLE_SIZE個数を超えるとこれもセグメンテーションエラー となります。 jtimeがTABLE_SIZE未満であることを確認するチェックが必要です。
お礼
ご回答ありがとうございます。 しかし 回答no.1の補足に記載したソースだけでも、セグメンテーション違反との表示が出てしましまして。。どーしたものかと。 とりあえずご指摘頂いた部分を見直したいと思います。
- Oh-Orange
- ベストアンサー率63% (854/1345)
★アドバイス ・多分 static な table[] にファイルから読み込んだ値を代入しているタイミングで エラーが出ていませんか? つまり、static int table[20]; の要素に 20 以上の値を代入したとか? ・補足にあるソースではエラーになるとは思えません。 本当に補足にあるソースでエラーが起こるのですか? ・以上。補足要求します。→fscanf() の入った main() を補足して欲しかった。
補足
ご回答ありがとうございます。 昨日の補足に一箇所誤りがありまして、mainが始まるトコの上部のexit(0);は実際にはありません。(今回の賞状には関係ないとおもいますが。) 以下がmain側のソースです。 int main(void) { char type; int ret; do { int findtime; // 0000形式に変換後の現在時間 int *search; // テーブル内の検索時間 int h; // 時 int m; // 分 int jtime = 0; int tmpd; FILE *file; time_t timer; // 現在時刻を取得し地方形式に struct tm *local; time(&timer); // 現在時刻の取得 local = localtime(&timer); // 現在時刻を構造体に変換 h = local->tm_hour; // 時 m = local->tm_min; // 分 findtime = h * 100 + m ; // 現在時間を0000形式へ変換 file = fopen( "jikoku.txt", "r" ); if( file == NULL ) { // ファイルが開かない場合(NULL返却)のエラー表示/エラー処理 puts( "jikoku.txtが開けません" ); return 1; } static int table [TABLE_SIZE]; // 時刻表のテーブル設定 // ↑(defineで40と指定) while (fscanf(file, "%d", &tmpd) == 1) { table[jtime++] = tmpd; if ( tmpd > findtime ) { // 検索した時刻と現在時刻(findtime)の比較 printf( "%02d:%02d\n\n", // 現在時刻より大きな値にヒットした時点で以下の処理 ( tmpd / 100), // 「時」取り出し ( tmpd % 100) ); // 「分」取り出し digitalLED(tmpd); // デジタル表示させる関数呼出、取り出した時刻を表示 break; } else if ( tmpd == - 1 ) { // テーブル内の-1に該当するならテーブルの頭(0610)を表示 printf( "%d\n\n", table[0] ); digitalLED( table[0] ); break; } } fclose(file); scanf("%c", &type); // Enter押下後再度プログラム開始 } while (type == '\n'); } 昨日の補足に記載したソースで、会社では動いていたと思うのですが。。。 処理系によって変わるのでしょうか。
- sakusaker7
- ベストアンサー率62% (800/1280)
テーブルを使うようにするのなら、あらかじめその大きさを決めておかなければなりません。 動的に領域を確保して大きくしていくという手もありますが、たぶんそこまでは進んでないと思うので省略します。 static int table[TABLE_SIZE]; のようにして適当な大きさで宣言します。 外部変数で宣言するという手もありますがそこはお好みで。 整数型の変数を二つ追加します。 int idx=0; int tmpd; 名前は一例です。お好きに付け直してください。 if( file == NULL ) { puts( "jikoku.txtが開けません" ); return 1; } while (fscanf(file, "%d", &tmpd) == 1) printf("%d\n", tmpd); table[idx++] = tmpd; if (tmpd == -1) break; } と読みながらテーブルに放り込んで行けばいいんじゃないでしょうか? 宿題としてエラー処理等を省いてますので、自分で書き直すときは忘れずに。 プリプロセッサを使って int table[] = { #include "jikoku.txt" } とかやる手段もありますが、提示されているデータファイルの 書式では無理ですし、こういうやり方は多分お叱りを受けるでしょうから これも省略します。
お礼
ファイルオープン時のエラー処理ですかね・・・ ありがとうございます!
- Oh-Orange
- ベストアンサー率63% (854/1345)
★アルゴリズムを少し変える必要があります。 ・以前は static int table[] というテーブルにデータがあったため、ポインタで 検索して表示すれば良かったが、外部ファイルならばそれに見合った方法に全体を 工夫する必要があります。 ・外部ファイルのテキスト内容で最後に -1 がありますが必要ありません。 理由は feof() 関数でファイルの終わりが分かるからです。 table[] 配列では終わりを示す -1 値がありましたが、外部ファイルの場合には feof() 関数で判断できるために必要なくなるのです。 ●本題 ・fscanf() から整数値を取得して画面に出力するサンプルを載せます。 これを作ってみて動作を確認して下さい。 その後に時刻表の検索に改良してみて下さい。 サンプル: #include <stdio.h> int main( void ) { FILE *fp; int data; if ( (fp = fopen("jikoku.txt","r")) != NULL ){ while ( fscanf(fp,"%d",&data) == 1 ){ printf( "%d\n", data ); } fclose( fp ); } return 0; } 最後に: ・fscanf() で取得した時刻値を読み込みと同時にチェックするようにすれば、いちいち static int table[] にデータをセットしてポインタで検索しなくても良い。 ・つまり、 int findtime = 905; ←現在の時刻の値 int check = 0; ←チェック用 while ( fscanf(fp,"%d",&data) == 1 ){ if ( data > findtime ){ check = 1; break; } } if ( check ){ /* data に検索した時刻値がセットされている */ } else{ /* 見つからなかった⇒最初の時刻を data にセットするなど */ } ←ここで LED などで表示するなど ・以上。参考に。
お礼
詳しくありがとうございます。 >static int table[] にデータをセットしてポインタで検索しなくても良い。 そう思っていたのですが、せっかくテーブルがあるのに何でつかわないのかと言う忠告を受けまして。 しかし、ご回答頂いた、読み込みと同時にチェックする等、ここまでアルゴリズムがしっかりしていれば良いモノができそうです。 頑張ります。
補足
お世話になってます。みなさんのおかげで形になってきました。 あとちょっとというトコで「セグメンテーション違反です」の表示がで、 プログラムが実行できません。 以下はデバッグの為、部分的に取り出したソースです。 #include <unistd.h> #include <stdio.h> #include <string.h> #include <time.h> #include <sys/io.h> #define PRN_PORTD 0x0378 #define PRN_PORTC 0x037a void lightLED( int num, int loc ) { static int table[] = { 0x3F, // 0 0x06, // 1 0x5B, // 2 0x4F, // 3 0x66, // 4 0x6D, // 5 0x7D, // 6 0x27, // 7 0x7F, // 8 0x6F, // 9 }; // table[ num ] で数値から変換 printf("num[%x] port[%x]\n loc[%d]", table[ num ], PRN_PORTD, loc ); outb( table[ num ], PRN_PORTD ); printf("num[%x] port[%x]\n", table[ num ], PRN_PORTC ); outb( loc, PRN_PORTC ); printf("num[%x] port[%x]\n", table[ num ], PRN_PORTC); outb( loc | 0x04, PRN_PORTC ); printf("num[%x] port[%x]\n", table[ num ], PRN_PORTC); outb( loc, PRN_PORTC ); } // 時刻表の時:分を表示する関数 void digitalLED( int time ) { int ret, four, three, two, one; four = ((time / 1000) % 10); // 4桁目を取り出す three = ((time / 100) % 10); // 3桁目を取り出す two = ((time / 10) % 10); // 2桁目を取り出す one = ((time / 1) % 10); // 1桁目を取り出す ret = ioperm( PRN_PORTD, 4, 1 ); lightLED( four, 0x03 ); // 時の10桁を表示 lightLED( three, 0x02 ); // 時の 1桁を表示 lightLED( two, 0x01 ); // 分の10桁を表示 lightLED( one, 0x00 ); // 分の 1桁を表示 ret = ioperm( PRN_PORTD, 4, 0 ); exit(0); } int main(void) { int time = 5555; / digitalLED(time); // 関数呼び出し return 0; } この辺りがあやしいとこまではこぎつけたのですが。。。 やはりテーブルに問題があるのでしょうか。 時間がある時にでも、みなさんのご意見を頂けたら幸いです。
お礼
「エラー処理」や、「関数を分ける」の意味がようやく分かりました。 ここまで親切にありがとうございます。 さっそくやってみたいと思います。 >サブ関数を利用して記述することを今後、心がけて下さい。 この様に記述して頂いた良い教科書があるので、勉強したいと思います。 ご覧頂いたみなさんのご協力に感謝します。 ありがとうございました。