Image
WELCOME TO MY HOMEPAGE
ぽてとのC言語練習中ページ
Image


●過去の更新履歴

Word.cの更新履歴です。

●Word.c 更新状況

2001/12/22 16:00 == Word.c version 0.08改訂版

  • こんどこそコンパイルできるはずです。

  • コメントのつけ方というのは、どういうコメントがよいのかわかんないので適当にやってました。「ここで単語の終わりを認識する」とか、そういう少し漠然としたことを書くといいんですね。以後気をつけます。

  • feof()とfgetc()との組み合わせでうまくいったというのはただの偶然です。これくらいしか思いつかなくて。

●Word.c 更新状況

2001/12/22 03:00 == Word.c version 0.08

  • ついに完成しました!ちゃんと単語を表示してくれます。

  • これからいろんなシチュエーションで試してみて、デバッグしてみます。またファイル名を読み込んだり、main()内の単語解析部分を別関数として扱うなど、まだまだやることはたくさんあると思います。

  • 完成して、やっと霧の中から抜け出せたような気がします。アルゴリズム理解能力、コーディング能力の無さを痛いほどに実感しました。

  • 「こんな機能をつけてみろ」とか、「こういう状況にも対応できるようにしろ」などのご意見も大歓迎です!


□このソースに対する239氏のコメント
コンパイル通らんぞ(藁。 
word008.c: In function `main': 
word008.c:78: `j' undeclared (first use in this function) 
word008.c:78: (Each undeclared identifier is reported only once 
word008.c:78: for each function it appears in.) 

while ((c=getchar())!=EOF)に関してはここを読め。 
http://www.catnet.ne.jp/kouno/c_faq/c12.html#1 

>   else if (i==0){/*英文字でなく、なおかつ以前も英文字でなかった時*/ 
>      ; 
>   } 
ふつーこういうのは条件を逆にするだろ。 

>   else{ /*英文字でなく、なおかつ前が英文字であった時*/ 
コメントが分かりづらい。要するに単語の終り? 
何をやってるかとか直接の条件なんてのは、コードを読めば分かることだから、 
コメントにはコードの意図を書いたほうがいい。 

feof()してからfgetc()してるのは…うーん、これのおかげで英字で終ってる 
ファイルでも正しく扱えてるんだが、そこまで考えてる? 

あとはまぁ、tempのオーバーフローのチェックとか、Node::wordを可変長にす 
るとか、argvからファイル名を取るようにするとか、そんなとこかな。 

●Word.c 更新状況

2001/12/22 00:00 == Word.c version 0.07 改訂版

  • 領域確保時、ターミネータの分を追加。

  • 文字列解析順序を変更。これで英字以外の文字が続いた時にも対応できるはず…。

  • コンパイラは文句をいいませんが、これそのまま実行するとアプリケーションエラー起こします。無限ループかなにか起こっているのかもしれません。どうもメイン関数内のaddNode()に渡すあたりが怪しいような…。


□私から239氏のコメント

>> Node root={"\0",0, NULL, NULL}; 
>空の単語も全部これにカウントすることになるが、それは意図的なもの? 
>Node *root = NULL;でnodeは要らないはず。 

addNode()で返ってきたものを逐一Nodeに再代入して使っているので、 
ルートの位置を保存しておく(あとでTraverseNode()で使う)ためにnodeを宣言していました。 
だからnodeをなくすとルートの位置が保存できなくなると思うのですが。 

>> while ((c=fgetc(fp))!=EOF || j<=MAXWORD){ 
>charとEOFの比較はうまくいかないし、jがMAXWORDにいっても終らん。 

これは…いままでおもいっきり while ((c=getchar())!=EOF) なんていう式を使っていたので 
それに倣っていました。でもEOFは0か1を返すのでcharと比較しても大丈夫なような…。 

>> ptr[j]=(char *)malloc(sizeof(char)*i); /*単語を格納するための場所確保*/ 
>> strcpy((char *)*ptr[j], temp); /*確保した場所に単語を格納*/ 
>ターミネータの分を忘れてる。このptr自体不要だと思うが。 
>二分木に貯め込むってのに、別に配列に取っておく必要はないだろう。 

ターミネータ('¥0'ですよね?)は忘れていました。 
ptrは…。ちょっと理解するのが難しいのでじっくり考えます。ノードのなかにポインタがあるから、 
それを使えば他のノードも指せるということですよね。(ptrを使わなくても) 

□私のレスに対する239氏のコメント
> addNode()で返ってきたものを逐一Nodeに再代入して使っているので、 
> ルートの位置を保存しておく(あとでTraverseNode()で使う)ためにnodeを宣言していました。 
> だからnodeをなくすとルートの位置が保存できなくなると思うのですが。 
addNode()が返してるもの自体がルートだろ? 

> これは…いままでおもいっきり while ((c=getchar())!=EOF) なんていう式を使っていたので 
> それに倣っていました。 
fgetc()やgetchar()の戻り値をなぜcharに代入しちゃいけないかは、 
はっきりいってFAQなので>>1にあるリンクを読んでくれ。 

> でもEOFは0か1を返すのでcharと比較しても大丈夫なような…。 
それと、EOFとfeof()かなにかを混同してないか。 

> ptrは…。ちょっと理解するのが難しいのでじっくり考えます。ノードのなかにポインタがあるから、 
> それを使えば他のノードも指せるということですよね。(ptrを使わなくても) 
「読み込んだ単語は全部ツリーにコピーされてるわけだから、 
それと別に保存しておく意味はないだろ」ということをいってるんだが、 
伝わってるかな。

●Word.c 更新状況

2001/12/20 20:00 == Word.c version 0.07

  • 単語抽出をメイン関数に組み込んで作成してみました。

  • 少々ポインタを使って、スタックオーバーフローの原因を減らしてみました。でもこのソースだとポインタ数に限界があるので、やはりオーバーフローが起こってしまいます。

  • 一応コンパイラには文句を言われなくなりました。しかしPCの不調からか、なぜか実行しても動いてくれません。たとえファイルオープンエラーにしても、なにも表示されずに終了してしまうのが不可解ですし。ですから、これで動くのかどうかまだ分かってません…。いろいろ試してみます。

□このソースへの239氏のコメント

> Node root={"\0",0, NULL, NULL}; 
空の単語も全部これにカウントすることになるが、それは意図的なもの? 
Node *root = NULL;でnodeは要らないはず。 

> while ((c=fgetc(fp))!=EOF || j<=MAXWORD){ 
charとEOFの比較はうまくいかないし、jがMAXWORDにいっても終らん。 

> ptr[j]=(char *)malloc(sizeof(char)*i); /*単語を格納するための場所確保*/ 
> strcpy((char *)*ptr[j], temp); /*確保した場所に単語を格納*/ 
ターミネータの分を忘れてる。このptr自体不要だと思うが。 
二分木に貯め込むってのに、別に配列に取っておく必要はないだろう。 

以前も書いたが、英字以外が連続したときのことを考えていない。 
iが何を意味しているか考えればできるはず。 
whileの中の条件の順序を変えた方がいいと思う。 

●Word.c 更新状況

2001/12/18 21:00 == Word.c version 0.06

  • 単語抽出関数GetWord()を作成してみました。

    メイン関数で単語格納二次元配列wordを準備し、改行文字までの文と格納回数を引数として渡し、関数中で解析してwordに直接単語をぶち込んでいます。これだとファイル中の全ての単語を格納しないとノード処理に移れないのでかなり不細工な関数になっています。wordに入りきらない単語の量を想定していませんし。

  • 一応メイン関数にGetWord()を組み込んでみました。しかし配列のポインタが全く分かってないようで、エラーでまくりです…。

    二次元配列を扱わずにGetWord()を作成したほうがいいというのはうすうす分ってるんですが…。


□このソースへの239氏のコメント

ツッコミどころ満載だが、細かいところから。 

> int GetWord(const char* str, int i, char **word){ 
>   char c; 
>   for (l=0; (c=str[l])!='\n' && (c=str[l])!=EOF ;l++){ 
文字列の終端はEOFじゃないし、charをEOFと比較してはダメ。 

>     tolower(c);  /*大文字→小文字*/ 
tolower()は副作用を持たない。 

>     if (!isalpha(c) && c=='\''){ /*cが英文字の時*/ 
これじゃc=='\''のときしか成立しない。 


次main()。 

>   while ((c=fgets(str,512,fp))!=NULL); 
ループの中が空だし、WORDMAXしかないstrに512も読んじゃいかん。 

>   i=getword(str, i, word); 
配列とポインタは違う。多重配列の場合、もっとも上位(宣言のときの左側)の 
配列だけはポインタに暗黙のうちに変換されるが、その下は一致しないとまず 
い。 

>   for (i=0; *word[i]<=len ; i++) 
この条件は何を意図してる? 

>     root=addNode(&root, *word[i]); 
rootはNode*じゃないからこの代入はできない。 
addNode()に関してはいろいろいわれて混乱してるのかも知れんが、 
少なくとも0.04のは正しい(はず。残してないからよく分からんが)。 

getword()に関しては、0.06の仕様よりも前の0.05改訂版の方がいいと思う。 
fpを渡す必要はあると思うが。 
0.06のGetWord()は複雑なわりにメリットがない。というかデメリットばかり。 

●Word.c 更新状況

2001/12/18 00:00 == Word.c version 0.05 改訂版

  • 昼間手直ししたソースです。
  • isalpha(),iの部分を修正しています。

  • あとはmain()内の単語抽出ができれば成功しそうな予感がします。

  • 文章から単語を配列にぶち込むのはなかなか難しくて…。
    すこし手間取っています。

 

●Word.c 更新状況

2001/12/17 22:00 == Word.c version stop.

  • 239氏、287氏にいろいろご指摘いただきました。
  • あまりに間違いが多いようで…。機能を分割して完成させていくことにしました。

  • まずはファイルの文章から単語を抽出することを考えています。
  • 条件判断が(私にとって)難しいので時間がかかりそうです…。

  • 一文字ずつ読み込んで判断すべきか、一文を読み込みバラして判断するかで迷っています。
    とりあえず一文字ずつ読み込んだほうが簡単な気がするので、そっちをまず実現させたいです。

  • ということで、まだソースは載せられません…。

  • あ、isalpha(c)の間違いは気づきました…。使うならif (!isalpha(c))が正しいですね…。

●Word.c 更新状況

2001/12/17 01:00 == Word.c ver0.05

  • 2chプログラム板C言語初心者スレでご指摘いただいた点と私の修正点は以下の通りです。
    239氏にはほんとに感謝です。

  • * makeNode()の引数にpはいらない
      ->引数pを削除させていただきました。

    * なんでaddNode()でmakeNode()を使わない
      ->確かにソースを見直したらmakenode()で代用できました。

    * addNode()でp==NULLのときを考慮してるから、 ルートノードをあらかじめ作成しておく必要はない
      ->main関数内のルートノード作成を削除しました。

    * if (c!=isalpha(c)) ってなんだゴルァ
      ->(c==isalpha(c))が正しかったですね。…ってそんな話じゃないですか?

    * 英字以外が連続したときのことを考えていない
      ->main関数内に状況保持変数stateを組み込みました。

    * malloc()を使うときは#include <stdlib.h>しる
      ->すっかり忘れていました…ごめんなさい。

    * strcpy()だとバッファオーバーランの危険がある
      ->現象の理解はできますが、対処(代替)方法が思いつきません…。

    * ファイル名は固定じゃなくてargvからとれ。あるいはstdin
      ->これは完成してからそのようにさせていただきます…。


  • 明らかな間違いを少し修正しました。

  • 現在はソースの修正段階です。

  • バグ取りでわからないことがあります。
     239氏に言われたとおり、ルートノードの作成をmakeNode関数に任せたのですが、そうするとTraversal()に渡すためにrootの位置を保持しておくことが出来ません。最初にmakeNode()に来た時に作られたノードの位置をなんとか保持できないかと考えましたが、関数を抜ける時に変数の値が消えてしまうようで…引数として位置をいじるべきか、グローバル変数として宣言しておくべきか混乱しています。

トップページへ