• ベストアンサー

C言語でタグの抽出について

main関数で定義した文字列 "<pro><name>chad smith</name><id>1234</id><live>America</live></pro>" があり第一パラメータで検索したいタグを入力すると その中身を表示するプログラムを作成したいと思います。 >>プログラム名 id 1234 >>プログラム名 name chad smith >>プログラム名 pro <name>chad smith</name><id>1234</id><live>America</live> のようになります。 自分なりに作成したのが以下の通りです。 #include<stdio.h> #include<string.h> #define BUF 256 int main(int argc ,char *argv[]) { char str1[]="<pro><name>chad smith</name><id>1234</id><live>America</live></pro>"; char *p1, *p2; int length; if(argc != 2) { fprintf(stderr,"\nUsage :プログラム名 検索したいタグ名\n"); return 1; } p1 = strstr(str1, argv[1]); if(p1==NULL) { fprintf(stderr,"\n検索したタグは見つかりませんでした。\n"); return 1; } length =strlen(argv[1]); p2 = strstr(p1 + length, argv[1]); fprintf(stdout,"\n%.*s\n", (p2-2) - (p1+length+1), p1+length+1); return 0; } 一応自分の期待通りに実行されますが あまり自分の中では良いプログラミングではないような気がします。 後半の部分の(p2-2) - (p1+length+1), p1+length+1あたりがわかりづらいと思います。 もっと良い方法がありましたらよろしくお願いします。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.5

タグ検索を行う際、基本的な部分に問題がある。 タグの開始は 「<」+「指定の文字」+「>」 でなければならない。 タグの終了は 「</」+「指定の文字」+「>」 でなければならない。 少なくとも、開始時は「<」「>」を、終了時は「</」「>」を含めて検索しないといけない。 元の文字列が char str1[]="<pro><name>chad smith</name><pr>no idia</pr><id>1234</id><live>America</live></pro>"; であった場合を考えてみよう。 現状のままでは >>プログラム名 id や >>プログラム名 pr の実行結果が以下のように「予想外の結果」になるでしょう。 >>プログラム名 id a</pr >>プログラム名 pr ><name>chad smith</name この辺りを考慮して書き直すと #include<stdio.h> #include<string.h> #define BUF 256 int main(int argc ,char *argv[]) { char start_str[BUF]; char end_str[BUF]; char str1[]="<pro><name>chad smith</name><pr>no idia</pr><id>1234</id><live>America</live></pro>"; char *p1, *p2; int length; if(argc != 2) { fprintf(stderr,"\nUsage :プログラム名 検索したいタグ名\n"); return 1; } sprintf(start_str,"<%s>",argv[1]); sprintf(end_str,"</%s>",argv[1]); p1 = strstr(str1, start_str); if(p1==NULL) { fprintf(stderr,"\n指定したタグは見つかりませんでした。\n"); return 1; } length =strlen(start_str); p2 = strstr(p1 + length, end_str); if(p2==NULL) { fprintf(stderr,"\n検索したタグは閉じていません。\n"); return 1; } fprintf(stdout,"\n%.*s\n", p2 - p1 - length, p1 + length); return 0; } こうすると「意味が判りづらい1とか2の定数」も不要。表示長の計算も「終了タグの開始位置 - 開始タグの開始位置 - 開始タグの長さ」、表示開始点の計算も「開始タグの開始位置 + 開始タグの長さ」となり、スッキリする。

windy0508
質問者

お礼

sprintfを用いて簡潔にまとめられるのですね。 そのことにより出力する際に指定もスッキリしていますね。 これで私の不満点が解決しました。 ありがとうございました。

その他の回答 (4)

  • S117
  • ベストアンサー率40% (18/45)
回答No.4

とりあえずソースをより自己説明的にしてみた。 #include<stdio.h> #include<string.h> #define BUF 256 #define TAG_START_LEN 1 /* `<' */ #define TAG_END_LEN 1 /* `>' */ #define CLOSE_LEN 1 /* `/' */ int main(int argc ,char *argv[]) { char str1[]="<pro><name>chad smith</name><id>1234</id><live>America</live></pro>"; char *first, *second; char *start, *end; size_t tagname_len, content_len; if(argc != 2) { fprintf(stderr,"\nUsage :プログラム名 検索したいタグ名\n"); return 1; } first = strstr(str1, argv[1]); if(first == NULL) { fprintf(stderr,"\n検索したタグは見つかりませんでした。\n"); return 1; } tagname_len = strlen(argv[1]); start = first + tagname_len + TAG_END_LEN; second = strstr(start, argv[1]); if(second == NULL) { /* 気になるのでエラー処理追加 */ fprintf(stderr,"\n検索したタグは閉じられていません。\n"); return 1; } end = second - TAG_START_LEN - CLOSE_LEN; content_len = end - start; fprintf(stdout,"\n%.*s\n", content_len, start); return 0; } 他の方が突っ込みを入れているとおり、HTMLやXMLから要素を抽出する処理になっていないのですが、とりあえず見やすくするテクニックとして。(なお、二個目がないときのエラー処理を追加しました。) ・変数名は初期化と使用する箇所が近いものを除けば、意味のない名前は避ける。 p1→first p2→second (本当はopen_tagとかになるが、現在は文字列を探してるだけなので、あえてこの名前) ・同じ式を何度も書いているのなら、その結果に何らかの意味がある。それを変数にして名前をつける。 p1+length+1 → start ・マジックナンバー(プログラム中に埋め込まれた、リテラル)はマクロ定数化する。そうすると、「何のために」それが使われているのか理解しやすくなる。 +1 → + TAG_END_LEN -2 → - TAG_START_LEN - CLOSE_LEN 変数・定数名はもうちょっといい名前があるかもしれませんが、この辺は好みもあるので、自分で考えてみてください。

windy0508
質問者

お礼

今回は練習ということでHTMLやXMLから要素を抽出する処理ではありませんでした。今回踏まえて今後取り組んで以降と思います。 変数、定数名に関してのご指摘ありがとうございました。 自分だけでなく他人にもわかるプログラムの作成が必要ですよね。 ご丁寧な書き込みありがとうございました。

  • asuncion
  • ベストアンサー率33% (2127/6289)
回答No.3

<id>に対する</id>のような、</何とか>が見つからなかった場合は どう対応するのでしょうか?

windy0508
質問者

お礼

そうですね、みつからなかったときの対処も考える必要がありました。 回答ありがとうございました。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

タグのネストは考えなくていいの?

windy0508
質問者

お礼

そうですね、考慮する必要がありましたね。。。 回答ありがとうございました。

  • koko_u_u
  • ベストアンサー率18% (216/1139)
回答No.1

>後半の部分の(p2-2) - (p1+length+1), p1+length+1あたりがわかりづらいと思います。 じゃあ、その辺を関数にするなりして、「意味」を持たせましょう。

windy0508
質問者

お礼

回答ありがとうございます。 sprintfを使用することにより簡潔になりました。