- ベストアンサー
ファイルから読み出し
ファイルを読み込んで画面表示するプログラムを作っているのですが、ファイルの扱い?がよくわかりません。 以下のフォーマット・書式とします。 ・フォーマット 番号__氏名__年齢 ・書式 番号:半角3桁まで 氏名:半角/全角問わずで半角20文字/全角10文字まで 年齢:半角2桁まで この場合に、以下のようにするとこの行で止まってしまいます。 特にエラーが出ていないのです。何故なんでしょう? fscanf(*fp,"%3d %20s %2d",&p->no,&p->name,&p->age);
- みんなの回答 (10)
- 専門家の回答
質問者が選んだベストアンサー
以前の質問も含め、この意味不明キャストは講師の指示な訳ですね。そこは了解しました。 #しかし酷い講師だなぁ……せめて名前くらいは役割に合わせればいいものを。 > 「文字列(半角スペース)文字列」の場合に、半角スペース後の文字列が > 次の数字と勘違いされてしまいます。 > これを分離せずに理解させる方法はないものでしょうか? scanf()系を使い、セパレータを半角スペースにしている限りは無理です。strchr()とstrncpy()の複合で文字列解析・分解するかなぁ。 ベタに「20文字+前後半角スペース」って決めうちなら fscanf(fp, "%3d%22s%3d\n", &no, name, &age); としておいてnameに入る前後スペースを後で除去する方向でもいけるかもしれませんが、私ならこの方法はとりません。
その他の回答 (9)
- Lchan0211
- ベストアンサー率64% (239/371)
> ・フォーマット > 番号__氏名__年齢 > 氏名:半角/全角問わずで半角20文字/全角10文字まで となっているのだから、無条件で20文字を取得するのは 出された課題の仕様に違反しているのでは? 半角プランクが出現した時点で氏名の終わりと認識する のが課題の仕様だと思いますよ。 FILE*をconst char *にキャストしている件についても、 課題内容をそのまま見せていただいてないので想像ですが、 たぶん課題の指示は、const char *で「ファイル名」を引き渡すこと であって、ファイルポインタ(FILE*)をキャストして渡せとは 書いてないのでは? もっと課題の仕様をよく読んで、思い込みの解釈をしないよう 気をつけるべきかと思います。
お礼
課題の指示では int file_read(const char* pFileName); を使う事が指示なのでいかに自分の思い込みが正しかろうと自分本位に作っては意味がないのです。 経験者というにも関わらず、実務経験が無いんじゃないでしょうか? こう言う形で提供してくれと言われたらどういう理由があろうとそう返すべきでは? そこで勝手に自分で変えて調整されてない内容の引数を渡したところでは本末転倒ですよ。 人は名前を書くときどうしますか? 田中一郎さんがいやとした場合、書き方は以下なだけあり、全てに対応する必要があります。 「田中一郎」 「田中(全角スペース)一郎」 「田中(半角スペース)一郎」 「ichiro tanaka」 後者の2つは正確に読み取れなくなります。 意図した動きをしないことが仕様違反です。 半角スペース以後を年齢と取る事が仕様とでもいいたいのですか? あなたも思い込みの激しい解釈をしたコメントはしない方がいいですよ。
- postal0x02
- ベストアンサー率42% (24/57)
malloc からの戻り値はキャストしましょう。 また malloc と free は対です。 確保したメモリはどこへ行ってしまったのでしょう???
お礼
>freeしていない理由 他の方へのコメントだけみればその理由がわかります
- arain
- ベストアンサー率27% (292/1049)
No.5 >さらにお聞きしたいのです。 >ファイルから読み込む際ですが、数字_文字列(半角20文字固定)_数字と並んでいる中、 >この文字列が「文字列(全角スペース)文字列」ならば大丈夫ですが、 >「文字列(半角スペース)文字列」の場合に、半角スペース後の文字列が >次の数字と勘違いされてしまいます。 fscanf()を使用し間に半角スペースがある限り回避は不能。 データフォーマットでそれぞれの項目が固定長であればfread()で取り込んで分割するといったことはできるけど…… No.6 >あなたにはあなたの教え方があるし、 >講師には講師の教え方がある、それで納得していただけませんかね? 「理解」はしますが「納得」は無理ですね。 >講師がダメであると言って何をしたいんでしょうか・・・・ 「何をしたい」ではなく「教えられてる生徒がかわいそう」とは思いますよ。 一番重要な「可読性」とか「設計の容易さ」ってところを台無しにしてかえってC言語をわかりにくくしてますから。 特に「学校」で「教える」ということであれば、捻ることよりも基本機能を理解し覚えることを重要視すべきであり、 今回であれば、「ファイルの読み込み」と「動的なメモリ確保(と構造体のポインタ操作)」の二点でしょう。 その部分を重要視すべきであり、引数の件はやるべきではないですね。 引数をやるのであれば、関数で「戻り値」や「値渡し」「参照渡し」といったところで行うべきですから。 本来こういった流れになるはずです。 ---------- (前提として、関数、構造体、ポインタ[メモリ操作]を学習している) 1.ファイルの種類、ファイルのオープン/クローズを学ぶ 2.ファイルの読み書きを学ぶ(fscanf、fread、fgetsなど) 3.ファイル名を別の関数に渡して、開いたファイルのファイルポインタを返す関数を作る 4.(3.のファイルポインタを利用して)ファイルの読み書きを行う関数を作る ---------- 類似の質問や、インターネット上にある「質問と同じような引数名のソースを使用している」C言語の説明サイトなどからすれば、 こんな感じで教えるのが妥当と思いますが。
お礼
あなたは何のためにこのQ&Aを利用しているのでしょうか? 見たところ土日祝日以外の昼間を中心に利用しているようですが、 社会人であれば労働時間帯ですよね。会社では2chが使えないから 利用できる掲示板で、相手の気持ち等無視して、自分の意見や考えを 無理矢理押し付けたい、そう取れます。 おそらくyahoo知恵袋も同就業時間帯に用いて同じ事をしているの だと思いますが・・。 あなたがやっていることは質問者の意図した回答ではなく、 あなた個人の思う意見の押し付けと他人に対する誹謗中傷以外の 何物でもないです。どうしても言いたい事を書くのを抑えて本分を アドバイスをするか、或いはそれさえも自制出来ないのであれば アドバイス自体を書くべきではないです。 非常に不快です。
- arain
- ベストアンサー率27% (292/1049)
No.5から 今回(前にもた類似質問でもの類似質問)の場合は、どう考えても、 「main()でファイルオープンしたファイルポインタを渡す」ではなく、 「main()ではファイル名入力だけ、ファイルのオープンと読み込みは別の関数で」と考えるのが引数の型や名前からも正しいように思えますよ。 >仕様とは最低限これを入れろと言われたら入れるべきであって、 >他により良い方法を知っているとしてもそうはせずに >あくまで入れろと言われた方法でやるのが筋ではないでしょうか。 > 「これ」とはどこを示していますか? 類似質問を見ても、「ファイルポインタを渡して読み込みだけ行え」とは言っていないと思いますよ。 本当にそういう指示があったのなら、その講師の力量を疑います。 (というか、実務経験から言えば、「疑う」とか以前に「使い物にならない」と判断しますが) 関数や引数、変数等の命名は、プログラムをする上で「わかりやすく」つけるのが基本です。 わざわざひねくれたことをして認識間違いによるバグを誘発させるような行為は一番やってはいけないことです。 >何かしら意図があるものと思ってます。例えば、キャストを覚える、等。 キャストを覚えるのであれば、こういった変なキャストは行いません。 行うとしても通常void型のポインタへの変換です。 >講師がおかしいと自分本位でやってしまっては、それこそ仕様違反だと思います。 私なら、まずはこの意味不明なことをする講師に疑問をぶつけますが。
お礼
実務と学校は違うでしょう。 実務でgetsやgotoは禁止ですが、学校では習いますし。 あくまで、基本的な遣い方だと思いますが。 あなたにはあなたの教え方があるし、 講師には講師の教え方がある、それで納得していただけませんかね? 講師がダメであると言って何をしたいんでしょうか・・・・
- D-Matsu
- ベストアンサー率45% (1080/2394)
> fscanf((FILE*)pFileName,"%3d %20s %2d\n",&p->no,p->name,&p->age); http://okwave.jp/qa4420902.html ↑でも指摘しましたが、関数のプロトタイプ宣言がint file_read(const char* pFileName)ならpFileNameには外でfopen()した結果のFILE *を無理やりconst char *にキャストして渡すのではなく、ファイル名を渡してfile_read()内でfopen()しましょう。これも課題なら、そう実装することが期待されています。 というか私が講師ならこんな実装を見たらたとえ動作してても落第にします。「仕様どおりに作ってない」ということで。 あと、pは(少なくとも初回は)NULLになってるのでsegmentation faultが発生するはずです。 ここはpではなくメモリを確保したばかりのmemが正しいのでは? pにmemを代入してもいいですが。 また、ループ終了条件が「ファイルを読みきった」であるはずですが、その判定が見当たりません。 ファイル終端に来てるのに検出せずに無限ループで回ってるのでは?
お礼
仕様とは最低限これを入れろと言われたら入れるべきであって、 他により良い方法を知っているとしてもそうはせずに あくまで入れろと言われた方法でやるのが筋ではないでしょうか。 何かしら意図があるものと思ってます。例えば、キャストを覚える、等。 講師がおかしいと自分本位でやってしまっては、それこそ仕様違反だと思います。 memで解決しました! でも、ファイルを読み込むとうまく出来ないんですよね・・ ずれて読み込まれてしまうようです・・ ループに関してはとりあえず1周して動くのを確認してから 追加する予定です。自信がないのでちょっとずつ作ってます。
補足
さらにお聞きしたいのです。 ファイルから読み込む際ですが、数字_文字列(半角20文字固定)_数字と並んでいる中、 この文字列が「文字列(全角スペース)文字列」ならば大丈夫ですが、 「文字列(半角スペース)文字列」の場合に、半角スペース後の文字列が 次の数字と勘違いされてしまいます。 これを分離せずに理解させる方法はないものでしょうか? 無条件で文字列20文字分を取得させるような方法ってないですか?
- arain
- ベストアンサー率27% (292/1049)
firstの定義と、LISTメンバが無いので推測ですが おそらくNO.2氏の通りでしょう。 ちなみに >first.next=NULL; > >p=first.next; >q=&first; > >while(p!=NULL){ >q=p; >p=p->next; >} pはNULLなので、whileの意味がありませんが。 ところで、「ファイルポインタ」を使用するのに(const char* pFileName)とする理由は? この記述なら通常は「ファイル名」もしくは「フォルダも含めたファイル名」の渡し方ですが。 # 最近、まったく同じ引数の名前とFILE型キャストしているのを見た気がする。
補足
同じクラスの人も質問してるのかもしれませんね^^ 色々と参考にさせていただいてます! >pはNULLなので、whileの意味がありませんが。 そうでした。
- postal0x02
- ベストアンサー率42% (24/57)
構造体定義とかもほしいですね。 first はグローバル変数ですか? あとコンパイルできません。 int file_read(const char* pFileName){ struct LIST* p,*q,*mem; int ret=0; first.next=NULL; p=first.next; q=&first; while(p!=NULL){ q=p; p=p->next; } do{ // メモリ領域確保 mem=malloc(sizeof(struct LIST)); if(mem==NULL){ printf("異常終了(メモリ不足)\n"); return -1; // 異常終了 } >&p->name >間違いでは? 実行してみましたが & を付けても付けなくてもエラーでませんでした。
お礼
こちらがグローバルです。 //********************定数定義 #define MAX 20 //********************構造体宣言 struct LIST{ // 線形リスト int no; // 社員番号 char name[MAX]; // 氏名 int age; // 年齢 int sex; // 性別 struct LIST* next; // 次の要素へのポインタ }; struct LIST first; // リストの先頭 &は外してました。すみません。 ファイルの方は、(_は半角スペースです) 123__あああああいいいい____20 といった羅列です。 メイン処理は、本関数がコールされるまではこんな感じです。 //********************メイン処理 int main() { int ret,i; char FileName[MAX]; // 入出力ファイル名 FILE *pFileName; // 入出力ファイルポインタ first.next=NULL; /***** 入出力ファイル名入力 *****/ printf("\n入出力ファイル名入力\n"); scanf("%s",&FileName); /***** 入力ファイルオープン *****/ if((pFileName=fopen(FileName,"r"))==NULL){ printf("異常終了(ファイルがありません)\n"); return -1; } /***** ファイルからリスト作成 *****/ ret=file_read((const char*)pFileName); if(ret==-1){ printf("異常終了(file_read)\n"); return -1; }
- postal0x02
- ベストアンサー率42% (24/57)
>ファイルの扱い?がよくわかりません。 どういうように分からないかが分からないので答えようがありません。 >番号__氏名__年齢 この表示だと 番号や氏名は _ で区切るのですか? それとも質問用に入れているのですか? >&p->name 間違いでは? &p->name ⇒ p->name
- arain
- ベストアンサー率27% (292/1049)
情報不足です。 最低でも、プログラムの最初からその行までは提示してください。
補足
_は半角空白です。 これでいいですか? int file_read(const char* pFileName){ struct LIST* p,*q,*mem; int ret=0; first.next=NULL; p=first.next; q=&first; while(p!=NULL){ q=p; p=p->next; } do{ // メモリ領域確保 mem=malloc(sizeof(struct LIST)); if(mem==NULL){ printf("異常終了(メモリ不足)\n"); return -1; // 異常終了 } fscanf((FILE*)pFileName,"%3d %20s %2d\n",&p->no,p->name,&p->age);
お礼
氏名の後の年齢が固定値(2桁数字)なので それを判断基準に入れてそうでなければ半角+名前の続きと 認識させて前の文字列につけるというのはどうでしょうか (まだ試していないです) そもそもファイルに登録されているということは それが正しい値である、という裏付けになるので 半角後の氏名が数字2桁で無い限りは氏名であると断言 出来る事になります。 まだ作成していませんが、新規で「番号__氏名__年齢」を 追加する際に、番号や年齢には入力制限を課すため、 入力保存したファイルを読み込む時には正しい値が ファイルに格納されているのは保証されると思ってます。