この人頭いいなと思ったエピソード

毎回お世話になっています。

ただいまC言語を勉強中の学生です。

基本的な文法は頭に入れたつもりでしたが、まだポインタと ファイルの入出力に苦手意識があり 実際うまくできないでいるので

どなたか ご存知の方にアドバイスいただければと思って書き込ませていただきます。

今回練習しているのは 先に述べたように、テキストファイルに

abc
def
ghi
jkl

などと文字列を書いておき、そのファイルを読み込んで
二次元配列に格納したいのですが、何を勘違いしているのか、
うまく実現できません。

参考書などを見て 一次元配列へ格納する場合は できたようなのですが、今後このプログラムを発展させる段階で 二次元配列に文字列を
格納できたほうが好都合なので 今模索しています。。。

ちなみに 一次元配列に格納する場合は 以下のように書きました。


#include<stdio.h>
#include<stdlib.h>

main()
{
char a[10];

FILE *fp;

fp = fopen("word.dat", "r");

if (fp == NULL)
{
printf("File does not exist.\n");
exit(1);
}

while(fscanf(fp,"%s",a)==1){
printf("%s\n",a);
}

fclose(fp);

return 0;
}

実はコレに関しても分からない点があり、それは 目的の配列変数
a[]に格納できたのだから それを確認したいと思い
printfで a[0]~a[10]を表示してみようとしたところ 変に文字化け
したものが表示されたり、何も表示されなかったり よく分からないことが起きます。私は何を勘違いしてしまっているのでしょうか(>_<)

長々と書いてしまいましたが、今回 御教授いただきたいのは

(1)、テキストファイルから 英字の文字列を読み込んで二次元配列に格納するために用いるのに 一番最適な関数はどれか(fscanfやfgetsなど) そして、その関数を使って 一番シンプルな記述をするにはどのように記述すればよいのか

(2)、上のようなプログラムの書き方で、配列aにテキストファイルから読み込んで格納できたようなのに、a[0]~a[10]をprintfしたときに
うまく中身が表示されないのはなぜか。

という点なのですが、(1)をとりあえず急いでおりますので、(2)はおまけ程度に考えていただければと思います。

なぜかファイルの入出力がいまだに把握できなくて苦手としておりますので、どうか よろしくお願いいたします。

A 回答 (6件)

#include <stdio.h>


int
main(void)
{
char a[10][10];
int i, maxi;
FILE *fin;

if ((fin = fopen("test.dat", "r")) == NULL){
fprintf(stderr, "Can't open file\n");
}
for (i = 0; i<10; i++) {
if (fgets(a[i], 10, fin) == 0) {
maxi = i;
break;
}
}
for (i = 0; i < maxi; i++) {
printf("a[%d] = %s", i, a[i]);
}

fclose(fin);
return 0;
}
    • good
    • 4
この回答へのお礼

ご親切に プログラムの書き方まで教えてくださったのにお礼が遅くなってしまい 大変申し訳ございませんでした!学生で 日々C言語を使っておりますが、普段使わない文法はどうしても苦手意識があり、路頭に迷っていました(>_<)そして、大まかには書けているのですが、把握しきれていないためにこまごまとバグがみつかるのですが どう間違っていてどう直せば良いのかというのが 本当に分からなくなっていたので 実際にソースを書いていただけて 読めて さらに理解することができました!本当に助かりました!ありがとうございました!

お礼日時:2007/10/10 22:32

二次元配列の件は他の方が模範を書かれているので省くけど、


>...それは 目的の配列変数a[]に格納できたのだから それを確認したいと思い
>printfで a[0]~a[10]を表示してみようとしたところ

そらあかんだろ。
10個しか用意してない配列に a[10] はダメよ。最大で a[9]までね。配列のインデックスは0から始まるから a[10] とやった時点で、配列はみ出しちゃうよ。

たぶんコンパイラの親切で「文字化けしたものが表示されたり、何も表示されなかったり」で済むけど、ふつうは「即死」だよ。

この回答への補足

回答ありがとうございます。
それは私の表記ミスです!
上ではa[0]~a[10]をprintfさせようとした。
と書いてしまいましたが、
実際にはきちんと

for(i=0;i<10;i++){
printf("a[%d]=%s \n");
}
としておりますので その点に関しては大丈夫です(^_-)☆
ご指摘ありがとうございます!

補足日時:2007/10/11 17:23
    • good
    • 0

>意図して代入したものでない 変な文字が入ってる可能性が


>あるということでしょうか?!
まちがえた。忘れてくれ。

>a[10][10]={"abc","def","ghi"}
>と同じ状況になるように 二次元配列aに格納するには
>どのように記述すればよいのか

先にも述べたように、「行単位」で処理する場合は fgets() を使った方がコードを読む人(自分を含む)が「1行ごとに何かをしてるんだな」とわかるので親切です。

要素数が同じだと説明しにくいので char a[5][10]; としよう。
これは「「要素数 10個の char 配列」の 5個の配列」です。a[0], a[1], .. , a[4] が各々 10個の char 配列ですね。

C 言語ではこれは同時に char[10] の先頭アドレス表現しているので
fgets(a[0], 10, fp); で a[0] から始まる char 配列に fp から読んだ文字列を格納できます。
(改行コードは自前でカットしてね)。以下 a[1], a[2], ... も同様。

もちろん、テキストファイルの行数や 1行のバイト数などはバッファオーバーランしないように適当にやっといてくれ。
    • good
    • 0
この回答へのお礼

お礼が遅くなってしまいすみません!(>_<)ご親切に何度もアドバイスしていただいて 本当にありがとうございました!本当に助かりました!独学では 苦手意識のある部分がどうしてもつかみきれずにいて路頭に迷っていたのです。アドバイスしていただいてイメージがしやすかったです!ありがとうございました!

お礼日時:2007/10/10 22:29

> printf("a[%s][%d]=%s \n",i,j,a[i][j]);


%sは文字配列の先頭要素(※)へのポインタ(型は char *)を要求しますが、
a[i][j]は文字(型は char)なので書式指定子と後続の引数が合っていません。
そのためa[i][j]に入っている「文字」を
『「文字列の先頭アドレス」と解釈して、どことも知れぬメモリ領域にアクセスしている』
ことになります。

※途中から表示したいなら別に先頭である必要はないけど、普通は先頭なので。

文字(char)を表示したいなら書式指定子は%cです。
ついでに最初の%sも%dの間違いでしょう。

しかし、printfが間違ってるのに
> chara[10][10]={"abc","def","ghi","jkl"};
> のように定義しておいて 実行させたところ、上手く意図したように
> 実行が成されたので、
というのは変ですね。こっちでは正しく書いていたのでは?


> ANo.2
> fscanf() は空白を読み込まないので、単語単位で読む場合にしか使えません。
スキャンセットに空白文字も含めればscanf系でも一応空白読めますね。
まあ行単位の処理ならfgetsを使う方が良いでしょうけど。
    • good
    • 0
この回答へのお礼

お礼が遅くなってしまい 大変申し訳ありません(>_<)
ご指摘いただいて、書式の指定が間違っていることに気がつきました!ありがとうございます!
はい、正しく実行が成されたというのは、一番メインだったのは計算の内容でしたので、printfが間違っていてもそれはそこの表示だけが間違っているだけなので問題にはしなかったためです!printfは 正しく計算されているか確かめるために付加したものでしたのでねっ!
とにもかくにも ご親切に教えていただいて本当に助かりました!どうもありがとうございました!

お礼日時:2007/10/10 22:26

>(1)、テキストファイルから 英字の文字列を読み込んで


>二次元配列に格納するために用いるのに 一番最適な関数はどれか
fscanf() は空白を読み込まないので、単語単位で読む場合にしか使えません。
fgets() は行単位で読んでくれるので汎用的です(逆に行を読んだ後の加工も自前でする)
いずれにせよ、ファイルを二次元配列に「どのように格納するか」は要件次第なので、それに応じてコーディングして下さい。

>a[0]~a[10]をprintfしたときにうまく中身が表示されないのはなぜか。
例えば a[0] には文字 'x' などが格納されており、それを書式指定子 %s にかけると、これを文字列の先頭アドレスと解釈して、どことも知れぬメモリ領域にアクセスしているからです。

この回答への補足

ご親切な回答 ありがとうございます!(>_<)
fscanfは 単語単位で読む場合しか使えない というのが
分かるようで分からないのですが、
読み込む元のテキストファイルに
abc(改行)
def(改行)
ghi(改行)
[EOF]
と書いている場合で
これらを 
a[10][10]={"abc","def","ghi"}
と同じ状況になるように 二次元配列aに格納するには
どのように記述すればよいのかと 困っております(>_<)
もう少しだけ アドバイスをいただけるととても助かります。
申し訳ございません(>_<)

(2)a[0]には 文字'x'などが格納されており というのは
意図して代入したものでない 変な文字が入ってる可能性が
あるということでしょうか?!最初に初期可をすれば
解消できるのでしょうか?!それとも どのような対策を
施せばこの問題をかいしょうできると思われますか?!(>_<)
大変 お手数をおかけしております(>_<)

補足日時:2007/10/08 10:50
    • good
    • 0

(1)について、


例えば、a[10][10]という配列があるとして、
この中に文字をどんな風に格納したいですか?

(2)について、
a[0]~a[10]を、どんな風にprintfしましたか?
なお、配列の定義でa[10]と書いたら、使えるのはa[0]~a[9]です。

この回答への補足

>(1)について、
>例えば、a[10][10]という配列があるとして、
>この中に文字をどんな風に格納したいですか?

はい。まず 最終的にはテキストファイルから読み込みたいのだけれど、読みこむ記述を足す前に、他の部分を完成させるために、
テストとして
chara[10][10]={"abc","def","ghi","jkl"};
のように定義しておいて 実行させたところ、上手く意図したように
実行が成されたので、後は テキストファイルから読み込むように
記述を追加させるだけ!と意気ごんだのですが ダメでした。。。
上のような記述をした場合は、a[0][0]にaが入っていて、a[0][1]にbが入っていると考えていたのですが、それが勘違いであるために(2)の
ような問題が起きているのでしょうか?!(>_<)

>(2)について、
>a[0]~a[10]を、どんな風にprintfしましたか?
>なお、配列の定義でa[10]と書いたら、使えるのはa[0]~a[9]です。

はい!
for(i=0;i<10;i++){
for(j=0;j<10;j++){
printf("a[%s][%d]=%s \n",i,j,a[i][j]);
}
printf("\n");
}
printf("\n");

と書きました。a[i][j]は最初にchara型として 定義していたので
%sという書式を指定しました。何がおかしいのか わかりますでしょうか(>_<)

補足日時:2007/10/08 10:14
    • good
    • 0
この回答へのお礼

すみません 再び 補足です(>_<)
上の書き込みに書き間違いを発見しました。。。
定義したのは
char a[10][10]です!(chara[10][10]と 書いてしまいましたが)

あ、はい それからa[10]まで定義した場合 値が格納できるのは
a[9]までであることは 理解しております(^^)

お礼日時:2007/10/08 10:24

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

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


おすすめ情報

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