トップページ⇒tech
54コメント36KB

添削願い

■ このスレッドは過去ログ倉庫に格納されています
0001名無しさん@お腹いっぱい。NGNG
C初心者ですが、おかしいところを指摘して下さい。
コンパイラはbcc5.5です。
/* ■ ではじまる行をファイルにリストアップする */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define TARGET_STRING "■" // ターゲット文字列
#define MAX_LINE 256 // fgetsの第2引数
#define MAX_COUNT 2 // strncmpの第3引数

void list_up(char arg1[]@`char arg2[]); //リストアップ関数

int main(int argc@`char *argv[])
{

if (argc!=3) {
printf("使用法:hoge <検索するファイル> <新しいファイル>\n");
exit(1);
}
list_up(argv[1]@`argv[2]);
return 0;
}

void list_up(char arg1[]@`char arg2[])
{
FILE *fp1@`*fp2;
char *str;
if ((fp1=fopen(arg1@`"r")) == NULL) {
printf("ファイルを開けません\n");
exit(1);
}
if ((fp2=fopen(arg2@`"w")) == NULL) {
printf("ファイルを開けません\n");
exit(1);
}
while(!feof(fp1)){
fgets(str@`MAX_LINE@`fp1);
if(strncmp(str@`TARGET_STRING@`MAX_COUNT)==0){
fputs(str@`fp2);}
}
fclose(fp1);
fclose(fp2);
}
00021NGNG
すみません2に書くべきでした
0003名無しさん@お腹いっぱい。NGNG
バッファに読み込めなかったときのエラー処理は?
後コメントの付け方が変なような気がするけど、趣味の問題だし。

インデントをtabじゃなくて全角スペースでやっといてね。
スレッドのタイトルも汎用的な奴を書いちゃうと後の人が困るし。
0004>1NGNG
char str[MAX_LINE];
0005名無しさん@お腹いっぱい。NGNG
げ、バッファ確保してないのか。
0006>5NGNG
char *str; ってバッファじゃねーの?
一番上のスレッドsageても意味ねーぞ。
00071NGNG
>3さん

ご指摘の数々、ためになります。ありがとうございます。
ですが、全角スペースはコピペしたときのコトを考えて、
わざとつけませんでした。やっぱりつけるべきですか?

>4さん、5さん

ありがとうございます。おかげで一行文字化けしてたのが直りました
(さっき気付きました)
しかしなんで配列にするとうまくいくんでしょうか?
ポインタでも一見うまく動いてるように見えるし・・・。
0008名無しさん@お腹いっぱい。NGNG
>6

あんたやばすぎ。
0009名無しさん@お腹いっぱい。NGNG
>7
Perlでタブと全角スペースを変換。問題なし。

宣言しただけのポインタにはいってるゴミアドレスに
読み書きしたら、動作はそのときの運次第。
0010>7NGNG
全角インデントのほうが見やすいです。
置換はエディタで一発。

んでポインタと配列だけど、
malloc() っちゅう関数はご存じ?
00111NGNG
>7さん
>Perlでタブと全角スペースを変換。問題なし。

そうですか。じゃ今度から全角スペース入れますね。
それと、ポインタの説明ありがとうございました。
もっと勉強しないといけませんね(汗)
00121NGNG
すみません。間違いました。11は9さんへのレスです。

>10さん
malloc()ってメモリを動的に確保する関数でしたっけ?
実はまだそこまで行ってません。
今度はもっと勉強して、もっと実用的なコードを載せたいと思います。
0013別の初心者NGNG
サイズを指定しないと、どれだけメモリ取ればいいか判断が
出来ないから、困る。で合ってますか?
でも配列にして要素数を指定しても、駄目なんですよね?

以前バッファが2つあるプログラムを書いたんですが、二つ目の
バッファに読み込むと最初のバッファが書き換えられちゃう事が
有って、アドレス調べてみたら、2つの領域が重なってる部分が
有りました。

で、そのときは、人に聞いたらstatic指定しろって言われて
それで解決したんですが、static指定するのって根本的な
解決手段なんですか?
調べてみても、それらしいことは見つからないし。。。
mallocを使えば根本的に解決することは分かってます。
0014名無しさん@お腹いっぱい。NGNG
自動変数の寿命のこと覚えなさい。
001513>14NGNG
関数内のローカル変数は、関数が消えれば、いっしょに消えるって
ことですよね? static指定すれば、関数が消えても値は残る。
なんか他にあるんですか?
0016名無しさん@お腹いっぱい。NGNG
>13
つーか、「バッファのアドレスが重なっている」ってのは、
別の意味のバグだと思うよ(サイズが小さかったとか)
あるいは、アドレス値を初期化してないとかね

片方を?staticで解決したのは、偶然だと思うんだけど
0017これはいいの?NGNG
while (!feof(fp)) {
  fgets(buff@` sizeof(buff)@` fp)
  printf("%s"@` buff);
}

[FILE.TXTの内容]
でーた1
でーた2[EOF]

[出力]
でーた1
でーた2


[FILE.TXTの内容]
でーた1
でーた2
[EOF]

[出力]
でーた1
でーた2
でーた2

最後の行が2度処理されるんだけど、いいの?
001817NGNG
>1 です。
忘れてました
feof()の仕様、つまり
「直前の処理でEOFに達したかを返す」を指摘したつもりです
0019たぶん>17NGNG
2度出力されるのは、fgetsの方がNULLを返してるのに見てないからかな
確かに、1さんはエラー等でもfgetsはNULLを返す場合があるから
適時判断した方がいいですよね
002013>16NGNG
やっぱり偶然でしたか。(偶然っていうと語弊があるけど。。。)
ありがとうございました。
0021名無しさん@お腹いっぱい。NGNG
fgetsって確か'\n'まで読み込む仕様になってるはずだから、
下の場合、データ2を読み込んだ後もファイルにeofが残ってる
つー解釈で動いてるからデータ2が2回出力されるんでしょ。確か
0022名無しさん@お腹いっぱい。NGNG
コメントの fgetsの第2引数 と strncmpの第3引数 が変って思うの俺だけ?
00231NGNG
ども1です。皆さん、レスどうもです。
確かに2回出力されますね。(全然気づきませんでした)
if (fgets(str@`MAX_LINE@`fp1)==NULL)
ってやれば一応二回処理は避けられるように見えますが、
これって完全なエラー処理じゃないような・・・。
やっぱ入門書だけじゃなく人の書いたソースを見て勉強しないといけませんね。
>22さん
自分でもおかしいと思いますが、最適な表現がわからないです。
//fgetsで読み込む最大文字数
// strncmpで比較するターゲット文字列のバイト数
これもおかしいですか?
0024名無しさん@お腹いっぱい。NGNG
255文字以上の行があると、出力の方が改行されないよね?
かぶってたらスマン。
0025名無しさん@お腹いっぱい。NGNG
入力の255文字目が全角文字の1バイト目だったら?
けっこうやな感じだよね‥

MAX_COUNTは素直にstrlen(TARGET_STRING)と書いたほうが
後々間違いが起こらなくていいかもしれないぞ。

「完全なエラー処理」なんてのはいまの1さんはまだあまり
気にしないほうがいいかもしれません。
入力ができなくなったときにそれがファイルの終わりなのかそうでないのかを調べて
終わりでなかったら「ファイルが読めません」くらいのメッセージを出すようにする
くらいでいいのではないでしょうか.
00261NGNG
>24さん
ウーム、そうですね。
とにかくmalloc()を早く覚えたいと思います。
>25さん
なるほど確かにstrlen使った方がいいですね。思いつきませんでした。
「完全はエラー処理」は出来るようになるまでだいぶかかりそうです。
とりあえずは指摘してもらった方法でやっていこうと思います。
ありがとうございました。
0027答えNGNG
Cで書くのがおかしい

{
if ($0 ~= /^■/) {
print $0;
}
}
0028もっと短くできると思うNGNG
while ( <STDIN> )
{
if ( /^■/ )
{
print;
}
}
hoge < file
0029名無しさん@お腹いっぱい。NGNG
>27
それをいいだすなら
grep "^■" で十分やん。
おまえ厨房過ぎ。
0030awkも仲間に入れてよNGNG
/^■/ {
  print $0;
}
0031今マイブームな書き方NGNG
/^■/&&print;
0032名無しさん@お腹いっぱい。NGNG
1さんの
>while(!feof(fp1)){
>fgets(str@`MAX_LINE@`fp1);
>if(strncmp(str@`TARGET_STRING@`MAX_COUNT)==0){
>fputs(str@`fp2);}
>}
みたいなのを作る時って自分は
while(fgets(str@` MAX_LINE@` fp1) != NULL){
  if(strncmp(str@`TARGET_STRING@`MAX_COUNT)==0){
    <省略>
  }
}

って「流れ」で作るんですけど問題ありますか?
せっかくなので添削お願いします。


・・・人に教わったこと無い(まあ普通はそうかな?)プログラマなもんで
とんでもない大間違いしていないかどうか、今でも不安なんです。
00331NGNG
>32さん
どうもココを読むとfeof()は使わないでいいみたいですね。
http://www.catnet.ne.jp/kouno/c_faq/c12.html#2
あのコードは「独習C」の前の版のヤツ見ながら書いたんですが・・・。
あ、それとPerlとか教えてくれた皆さんありがとうございました。
スクリプト言語もなかなかいいですね。勉強してみます。
0034名無しさん@お腹いっぱい。 NGNG
http://mentai.2ch.net/test/read.cgi?bbs=prog&key=962184288
↑のスレッドを参考に2chのdatのファイルリストからdatを読み込んで
htmlに変換、インデックス作成ってのを作ったんだけど、載せたら
見てくれます? 350行くらいあります。。。
カンマ置換と、リンク張りまでは出来てます。

>>999みたいな奴が相対パスになってるからそれを直すのがまだやってない。
タグが直接datに書き込まれてる。
<a href="#999">みたいに書き換えたいんだけど、レスを1件ごとに
書き込みしてるから<a name="999">を埋め込むのがめんどくさい。
<a name="***">みたいなやつが10000個くらいあってもブラウザ大丈夫かな?
全部に埋め込めば、楽に出来るんだけど。。。
0035名無しさん@お腹いっぱい。 NGNG
貼っとけ。嫌なら見なきゃいいだけだから気にするな。
0036名無しさん@お腹いっぱい。 NGNG
34はどうした?
俺も必ず目を通すから(レスするかはわかんないけど)、ソース載せてみなよ。
俺も、ヘルプもろくに読んでいない「教えてください」に答えるより、
よほど有意義だと思うから。
003734 NGNG
環境はbcc、win2kです。
ソースは↓です。
http://www.geocities.co.jp/MusicHall/6045/dat_to_html_h.html
http://www.geocities.co.jp/MusicHall/6045/dat_to_html_c.html
フォルダを指定すればdatを読み込むように拡張しました
一応動きますが、今日急いでやったので変なところがあるかも。

フォルダの中のファイルを読み込むのをFindFirstFileを使って
やったけど、他でC++のソースを見ただけで、仕様を全然分かって
ない状態で作ったから、変かも。

htmlの知識はほとんど無いです。タグとかが変かも。。。
あと、フォルダの作り方が分からないです。
存在しないフォルダで"w"属性でファイルオープンすれば
フォルダも作ってくれると思ってたのに。。。
0038名無しさん@お腹いっぱい。 NGNG
VBだったら、4時間くらいで、
GUIのナイスガイつくれるぜ。
0039名無しさん@お腹いっぱい。 NGNG
プログラムって女性形なんじゃないの?<ナイスガイ
0040>34 NGNG
軽く眺めただけなのだが、いくつか気がついた点を。
細かい事もあるし、優先順では無いよ。

フォルダの作り方  mkdir を調べるべし。

#include  <...>  //システムのヘッダファイル <windows.h> <stdio.h>
#include  "..."  //自作orプロジェクトのヘッダファイル "dat_to_html.h"

main()
メッセージは、行の途中に'\n'が混じっていると見辛い。
printf("...ディレクトリを引数にして実行\n"
   "複数のディレクトリを指定することも可能\n");
(空白はまとめられ、隣接するリテラルは連結される。)
あるいは、メッセージを別に持つのも1つの方法。後述のwrite_html_headerも参照。

main_rtn()
end_flgとwhileループは、do-whileに置きかえるべき。
流れの基本は、(ファイルが見つからないときにエラーを返さないのなら)
if (FindFirstFile()) {
  do {
    ...
  } while (FindNextFile());
}
その他、メッセージが適切か、エラー時のFindCloseは、とかもある。

read_title_from_dat()
ローカルな配列に一行読みこんで、
strchrかstrtokを使う方がスマートじゃないかな。
0041>34 NGNG
check_dat_name()
isdigit()を使った方がよい。ただし、日本語の扱いは注意。
それと、この関数内でエラーメッセージを表示しているけど、
この場合は呼出元で表示した方がいいね。
できるだけエラーを表示する階層は統一したほうがいい。
(FindFirstFileで"?????????.dat"を指定すれば、長さのチェックは不要)

Msearch()
strstrを調べよ。

Mreplace()
ループの直前と最後に似たような処理があるのはいただけない。
p = text とか p += n とかを混ぜたい。
というより、俺なら、こんな感じにする。
char *p = text;
int srclen = strlen(key);
int dstlen = strlen(rep);
while ((p = strstr(p@` key)) != NULL) {
  memmove(p + dstlen@` p + srclen@` strlen(p + srclen) + 1);
  memcpy(p@` rep@` dstlen);
  p += dstlen;
}

skip_to_comma()
strchrを調べる。あるいは、strtokを使う。
Mreplaceの仕様は「より長い文字列への変換」をサポートしているが、
ここを見ると、直接やるのは難しいね。
0042>34 NGNG
find_end_of_url()
文字コードに依存した書き方はよくない。
(しかも、直接32とかの数値を書くのはまずい。せめて' 'に)
isprintとか、isspaceとか、is...系を調べてみる。

write_html_header()
コード中の文字列内に様々な記号が出てくるね。修正があったら大変。
void msgout(char **table@` FILE *fp)
{
  while (*table) {
    fprintf(fp@` "%s"@` *table);
    table++;
  }
}
みたいなのをつくって、テーブルを引数にして呼び出したらどうだろう。
static char *head_text[] = {
  "<html><head>"@`
  "<meta http-equiv=\"Content-Type\" content=\"text/html;"
  " charset=x-sjis\">" "\n"@`
  "<title>"@`
  NULL@`
};
static char *color_text[] = {
  ...
  NULL@`
};
004334 NGNG
>39-42
レスありがとうございます。直してみました。
http://www.geocities.co.jp/MusicHall/6045/dat_to_html_c9_5.html
http://www.geocities.co.jp/MusicHall/6045/write_html_9_5.html
http://www.geocities.co.jp/MusicHall/6045/dat_to_html_h9_5.html
初めてソース分割してみました、こーゆー感じでいいのかな?
一人で作業してて、グローバル変数使わないと分割しても
意味無いような気もするけど。。。この考えかたって間違ってる?
0044>43 NGNG
ソース分割について。
分割するときは、機能別に分ける。入力・出力・ユーティリティ・メイン等。
そして、他のファイルから呼ばれない関数はstaticにする。
staticではない関数を、共通のヘッダファイルに宣言する。
そうすれば、やりとりのインターフェースがみえてくる。
(というより、先にインターフェースを決めるのが普通)
今回は、出力処理を分割しているけど、そんな感じで良いと思う。

これに関連して、全ての関数宣言をヘッダで行っているけど、
これは必要ないよね。内部関数は、.cファイルの先頭で充分。
あと、スタイルの問題だけど、main()をソースの一番下に書いて、
呼び出される関数の中身を上の行に書くスタイルだと、
先頭で宣言する必要もなくなるよ(スタイルは好みの問題)。
0045>43 NGNG
さて、前回のレスで、余計に混乱させたらしい点から。
以下のようなプログラムを作って、実行してみよう。
argv[argc] がNULLである事を利用している。
コマンドライン引数を変えて、試してみること。
void msgout(char **table)
{
  while (*table) {
    printf("%s\n"@` *table);
    table++;
  }
}
int main(int argc@` char *argv[])
{
  static char *table[] = {
    "1行目"@`
    "2行目"@`
    "終り"@`
    NULL@`
  }
  printf("引数の個数 計%d個\n"@` argc);
  msgout(argv);
  printf("\n");
  msgout(table);
  return 0;
}
テーブルの最後にNULLを置いて「これでお終い」の目印とし、
NULLまでの全ての「ポインタの配列」を出力する。
0046>43 NGNG
もうひとつ、前回の補足。Mreplace()に以下の流れがある。
p = strstr();
while (p!=NULL) {
  ...
  p = strstr();
}
この、同じ関数の呼出がなんとなく引っかかる。

p = text; // この後、textが一度も出て来ないので、本来不要。
while ((p = strstr(p@` key)) !=NULL) {
  *p = '\0';  // この行は不要
  strcpy(buf@` p+m);
  strcpy(p@`rep);  // pの位置に「コピー」
  strcat(p@` buf);  // 〃追加
  p += n;  // 次に検索を開始する位置をずらす
}
write_res()にも、同じ流れがあるね。
0047>43 NGNG
あと、前回よく読まずにやめてしまったところ。

read_dat()
途中に、こんなif文がある。
 if (*(res + MAX_SIZE_RES - 2) == ('\0'||'\n'))
やりたいことは分かる。
でも、よく考えてみよう。比較式の右辺の値は何になる?
答えは1。つまり、'\x01'。
ちなみに、右辺の括弧を取り去ると、ifの条件が常に真となり、
結局、思った通りには動いてくれない。

ここは素直に、(改行まで読みこんだか判断するために)
 if (strchr(res@` '\n') == NULL)
を使おう。もちろん、最初の
 *(res + MAX_SIZE_RES - 2) = '\0';
も不要になる。
(最初、この部分の意味が理解できなかったよ)

あと、この関数の主要な部分である、切りだし処理を考えてみる。
元のソースでは、一単位毎にエラーをチェックしているね。
だけど、方法としては、「とりあえずデータを入れておいて、
まとめてエラーチェックをする」という手もある。
後で例を出すよ。

とりあえず、すぐに気がついた点だけ触れたけど、
まだ読んでいる最中だから、後で(夜か明日)またレスするね。
004847 NGNG
(続き)
関数(処理)の切り分けは、そんなにおかしな点はないと思うけど、
read_dat()を考えていたら若干気になったので、まずそれから。

かなり直していると思うけど、コード中にマジックナンバーを
含める個所は、出来るだけ減らそう。
例えば、Mreplace(str@` "@`"@` "@`")が2箇所出てくる。
こういうのは、間にワンクッションはさみたい。

Mreplaceという関数は、出来るだけ汎用的につくる。
そして、convert_text(char *str)のような関数を作り、
その中でMreplace(str@` "@`"@`"@`")を呼ぶ。
そうすれば、(例えば)将来タブを全角空白に展開する機能を
付け加えたくなっても、その一箇所だけ変更すれば良い。
(構造体のテーブル形式にすれば、変更は更に楽)

これは、処理を日本語で考えればわかる。
やりたいこと=「テキスト中の代用文字列を変換する」
      →テキスト中の"@`"を"@`"に変換する
      →"@`"と"@`"をパラメータとして、変換処理を行う
メインルーチンでは、この
「テキスト中の代用文字列を変換する」関数を呼び出す。

※実際には、skip_to_commaの中で呼び出しているから、
2箇所で済んでいるだけ。skip_to_commaが、
「文字列の切り分け」と「テキストの変換」の
2つの処理を行っているって事だね。
004947 NGNG
dat_to_html()内のループ。
レス1の処理とか、エラーの処理とかで結構ごちゃごちゃしてるね。
まず、一行毎に読みこんで処理する場合は、
 while (fgets(buff@` sizeof(buff)@` fp) {
   ...
 }
が定石と言っていい。
厳密なエラーチェックが必要なら、ループを抜けた後に、
 if (ferror(fp))
で処理を書くけど、通常はそこまでは必要無い。
読みこみでエラーが起きても、データの整合性は保てるからね。
個人レベルで処理をする場合、データの保持だけを考えれば良い。
むしろ、書きこみ時のほうがエラーが起きたときに重大な上、
空き領域が無くなる等、エラーが起き易いので気をつけなくてはいけない。

次に、レス1だけタイトルを出力する必要があるのだから、
ループ内で判断しないで、素直に外に出そう。
流れは、
 レス1の読みこみ
 レス1の処理
 ヘッダを出力
 レス1を出力
 while (レスnの読みこみ) {
   レスnの処理
   レスnを出力
 }
(コンマ演算子を使って出力をwhileの条件式に入れる事も出来るけど、
あまり勧められない。)

あと、かなりいじる事になるので、これ以上は考えものだけど、
もしread_dat(名前が処理内容を表さなくなっている)を
 エラーが無ければ、titleへのポインタを返す
 エラーがあれば、NULLを返す
という仕様にして、
titleが空文字列であるかを呼出元でチェックすれば
res_element構造体のメンバから、titleを外せるね。
さらに、write_htmlの引数にレス番号を加えれば
res_numも必要無くなる。

と、ここまで書いて、前のソースを眺めてみたら、
以前は、タイトルの読みこみを別に扱っていたんだね。
それを、似た処理だからまとめたのか。
005047 NGNG
read_date()で、まとめてエラーチェック。
strtokを使ってみたら、連続する@`@`をうまく処理できなかったので、
自前で用意してみる。('@`'専用)
#if 1 〜 #else 〜 #endif の部分は、
前述の「titleへのポインタを返す」形式にする場合。

// 文字列をtokenに区切り、次の文字列の先頭を返す。
// NULLが与えられた時はNULLを返す。
// 次の文字列が見つからないときはNULLを返す。
char *next_token(char *text)
{
  char *p;
  if (text == NULL)
    return NULL;
  p = strchr(text@` '@`');
  if (p)
    *p++ = '\0';
  return p;
}
int read_dat(struct res_element* Pres@` char* res@` FILE* datfp)
{
  char *p;
  ...
  p = strchr(res@` '\n');
  if (p == NULL)
    return READ_ERROR;
  *p = '\0';
  Pres->name = res;
  Pres->mail = next_token(Pres->name);
  Pres->date = next_token(Pres->mail);
  Pres->res = next_token(Pres->date);
#if 1
  Pres->title = next_token(Pres->res);
  if (Pres->title == NULL || next_token(Pres->title) != NULL)
    return READ_ERROR;
  if (Pres->res_num != 1 && *Pres->title)
    return READ_ERROR;
#else
  p = next_token(Pres->res); // pは使いまわし
  if (p == NULL || next_token(p) != NULL)
    return NULL;
  // タイトルが空文字列かのチェックは呼出元で行う
#endif
 
  Mreplace(Pres->name@` "@`"@` "@`");
  Mreplace(Pres->mail@` "@`"@` "@`");
  Mreplace(Pres->date@` "@`"@` "@`");
  Mreplace(Pres->res@` "@`"@` "@`");
#if 1
  Mreplace(Pres->title@` "@`"@` "@`");
  return 1;
#else
  Mreplace(p@` "@`"@` "@`");
  return p;
#endif
}
こういう流れにしてみる事も考えよう。
005147 NGNG
機能の分割と考えれば、リンクを貼る処理も分けてもいいね。
void write_link(const char *str@` const char *link@`
  const char *target@` FILE *outfp)
{
  fprintf(outfp@` "<a href=\"%s\""@` link);
  if (target)
    fprintf(outfp@` "target=\"%s\""@` target);
  fprintf(outfp@` ">%s</a>"@` str);
}
こんなのを作っておいて、
write_html()
{
  char buff[適当な大きさ];
  ...
  else { // メール欄が空白じゃない時
    sprintf(buff@` "mailto:%s"@` Pstruct_res->mail);
    write_link(Pstruct_res->name@` buff@` NULL@` htmlfp);
  }
  ...
}
write_res()
{
  char buff[適当な大きさ];
  ...
  while ((start_url = strstr(end_url@` "http://";)) != NULL) {
    fprintf(htmlfp@` "%.*s"@` start_url - end_url@` end_url);
        // リンクの前までを出力
    end_url = find_end_of_url(start_url);
    sprintf(buff@` "%.*s"@` end_url - start_url@` start_url);
        // strncpyだとうまくいかないので
    write_link(buff@` buff@` "_blank"@` htmlfp);
        // リンク部分を出力
  }
  fprintf(htmlfp@` "%s"@` end_url);
  // ポインタの差とか、printf系の"%*s"を使っているので、
  // 知らなかったら調べてみてね。
}
write_index_html()  ・・・略

write_link()のような処理を作っておけば、将来機能拡張して
>43 とか
>43 とかに
自動的にリンクを貼りたくなった時も対応出来る。
もちろんその時は、write_html()の中で
"<a name="RESxx"></a>"等を出力しないといけないけど。
005247 NGNG
全体を通して、変な癖とかはないと思うし、
色々試しながら小技等も覚えていけば良さそうだね。

印象としては、
・ポインタは理解しているけど、使いこなせているほどではない
 ポインタの配列や構造体の配列、加減算等を
 色々ためしてみよう
・標準関数に知らない物が多い
 リファレンスのような本でも買って、流し読みしてみよう。

他人のソースを読んでみよう。
出来れば日本語の丁寧なコメントつきがいい。
きれいなソースなら、テクニックや定石的な処理も学べるし、
汚いソースでも、「自分ならこうする」と考えればいい。

ここまでも、いくつか機能拡張のアイディアを出してみたけど、
その他にも、
・index.htmlに、レス数や最終レスの時刻を加えたい。
・index.htmlを、時刻順に並べたい。
 それも、datファイルの時刻ではなく、最終レスの時刻にしたい。
・特定時刻以降のレスだけを表示させたい。
 特に、前回読んだ以降のレスだけを表示してみたい。
などの考えが浮かぶね。

それらを実現するには、メモリの動的確保も覚えなくちゃね。
ポインタを理解していれば、全然難しくないよ。
構造体も積極的に利用しよう。
時刻順に並べたりするには、.datから必要なデータを読んでおいて、
構造体に保持する。「前回読んだ以降」を実現するときは、
まとめて別ファイルにでも保存しておこう。
データを読む時、スレッド毎に連結リスト等を作って保持し、
全てを読んだら、ポインタの配列にする。
その配列をqsortでも使って並べ替えればできあがり。

と、簡単に書いたけど、何の事か分からないかもしれない。
構造体の配列、構造体へのポインタ、構造体へのポインタの配列。
この辺りが大体理解出来ているなら、次の段階へ進もう。
もう、今更言うまでもないけど、読むべき本は、
「Cプログラマのためのアルゴリズムとデータ構造」。
(既に読んでたらごめんね)

俗に言う「糞プログラム」が生まれるのは、
文法とライブラリ(API)は覚えても、
アルゴリズム+データ構造=プログラム
を理解していない人が多いから。
そんな人にはならないようにね。大丈夫だと思うけど。
005347 NGNG
ぐをー、全部書きこんで読みなおしたら、
Mreplace(str@` "@`"@` "@`"); が
Mreplace(str@` "@`"@` "@`"); になっていた。
許して。
005434 NGNG
時刻順にindex作るのは線形リストを繋いでってまとめて出力するんですよね
アルゴリズムの勉強をしてる最中で、線形リストを使ったプログラムを
書いてみたかったのでちょうどよいです。

アルゴリズムの本は「C言語によるはじめてのアルゴリズム入門」を使ってます。
4月から独学で勉強を始めて、最初は友人に貰った古い版の独習Cでやって
最近K&Rと↑のC言語による〜を買って、今はアルゴ中心にチマチマ読んでってます。
ソースはプログラマ板のソース公開ページスレッドをみて探してみます。

こんなに丁寧な添削、解説して貰えるなんて思ってませんでした。
ありがとうございます。金土に書き直し、拡張してアップします。
明日はテストが有る。。。
■ このスレッドは過去ログ倉庫に格納されています