- ベストアンサー
C++で指定文字列のカウント方法(テキストマイニング)
大学でC++ builder を使った授業を受けておりましたが、いつも早く終わっていた(教科書を写すだけなのでタイピングが早ければすぐ終わる)のをC++が得意と勘違いされ、試験を免除する代わりにテキストマイニングに関連するソフトを作るという課題を言い渡されました。 テキストマイニングの意味は大体理解し、掲示板などからとってきたテキストデータの中にある「美味しい」と「不味い」の文字列の個数を数え上げるプログラムを作ろうと考えていますが、やり方が全くわかりません。授業では「学生のためのC++builder」という主に数値計算を扱った教科書の例題を実行するというもので、テキストデータの扱い方はほぼまったくしていません。 今までにやった中でファイルを扱ったようなものとしては void __fastcall TForm1::Button1Click(TObject *Sender) { OpenDialog1->Execute(); //入力ファイル用ストリームのオブジェクト(fin)を生成する ifstream fin; fin.open(OpenDialog1->FileName.c_str()); //エラー対策 if(!fin){ ShowMessage("ファイルのオープンに失敗しました!!"); exit(1); } double sintyo; char name[20]; //ファイルから氏名、身長データを読み込む fin>>name>>sintyo; while(!fin.eof()){ StringGrid1->Cells[0][n]=(String)name; StringGrid1->Cells[1][n]=FormatFloat("###0.0",sintyo); data[n]=sintyo; n++; fin>>name>>sintyo; } fin.close(); } だけであり、それを元にして改良するということができそうにありません。 最初からチェックしていって、「美味い」など指定文字列があればdに1を加えるなど、アルゴリズムを考えること自体はできるとは思います。 しかも期限は一週間となっています。 どなたか方法をお教えいただけませんでしょうか。
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
完全に答えですけどプログラム作ってみました。 ***実行前に行う事*** 実行ファイルと同じフォルダに test.txt というファイルを置いてください。 これは検索の対象となる文章を書いたファイルです。 よくわからない場合は以下の手順にそって行ってください。 まず 「C言語入門。C言語について学んでいます。」 と書いて test.txt という名前で保存。(保存する場所は実行ファイルと同じフォルダ) 以下のプログラムをコンパイル #include <stdio.h> #include <string.h> #define BUFFER_SIZE 10000 char string[BUFFER_SIZE]; void kensaku(void){ struct s{ char st[100]; int counter; }; struct s string2[100]; int i=0,j=0,n,count=0,h_count=0; char string3[100]; printf("検索終了を示す文章を入力してください。(この文章は検索出来ません)\n"); scanf("%s",&string3); printf("設定完了。検索終了時には「%s」と入力してください。\n",string3); while(1){ printf("\n*********************************************\n"); printf("検索したい文章は?(終了は「%s」と入力)\n",string3); scanf("%s",&string2[h_count].st); if(strcmp(string2[h_count].st,string3)==0) break; n = strlen(string2[h_count].st); while(string[i]!='\0'){ if(string[i]==string2[h_count].st[j]){ j++; if(j==n) count++; } else{ j=0; } i++; } printf("「%s」の検索ヒット数[%d]回\n\n",string2[h_count].st,count); string2[h_count].counter=count; h_count++; i=0; count=0; } printf("検索終了\n\n"); i=0; while(i<h_count){ printf("「%s」\tの検索ヒット数\t[%d]回\n",string2[i].st,string2[i].counter); i++; } return; } void main(){ char string2[BUFFER_SIZE]; FILE *fp; fp = fopen("test.txt", "r"); if ( !fp ) printf("ファイルオープンエラー\n"); else{ while( fscanf(fp, "%s" , string2) != EOF){ strcat(string,string2); } } kensaku(); fclose(fp); } コンパイル画面に最初 「検索終了を示す文章を入力してください。(この文章は検索出来ません)」 と出ますから何か自分で決めたキーワードを入力してください。 (例:finish) 次に 「検索したい文章は?」 と出ますから、そしたら 例えば「言語」と入力してエンター押します。 すると 「言語」の検索ヒット数[2]回 と表示されます。また 「検索したい文章は?」 と出ますから連続で検索したい場合はまた、続けます。 終わりたい場合は最初に決めた文字列を入力。例ではfinish。 すると検索結果一覧が表示され、終了します。 一度コンパイルしてみて、実行できるかどうか試してください。 注意:なお、プログラムは見やすいように、tabインデント(字下げ)が全角スペースに置換してありますから、 お使いのソフトで全角スペースをtabインデントなどに置換してからコンパイルしてください。 全角文字があるとエラーになると思うので。 コンパイルできなかった場合や、解らない事があった場合はまた聞いてください。
その他の回答 (4)
- KoHal
- ベストアンサー率60% (110/181)
BorlandC++Builderを使ってるならAnsiStringを使うという選択肢もありますね。 int SearchWordInString( const AnsiString& src, const AnsiString& word ) //srcからwordを検索しその数を返す { int count = 0, at = 0, len = word.Length(); while ( int pos = AnsiString(src.c_str()+at).AnsiPos(word) ) { ++count; at += pos-1+len; //AnsiString::AnsiPosは1始まり } return count; } //呼び出し関数(どこかのイベントハンドラ)内で String word1 = "美味しい", word2 = "不味い"; //TEditあたりで取得可 TStringList* list = new TStringList; list->LoadFromFile( "sorce.txt" ); //ファイル名はTOpenDialogで取得可 int count1 = SearchWordInString( list->Text, word1 ); int count2 = SearchWordInString( list->Text, word2 ); ShowMessage( word1 + "の検索結果:" + count1 ); ShowMessage( word2 + "の検索結果:" + count2 ); delete list; C++で文字列を扱う場合、複数の選択肢があります。 1.Cで標準的なchar*を使う No.3の方の回答 2.C++標準ライブラリのstd::stringを使う No.2の方の回答 3.開発環境独自の文字列オブジェクトを使う BorlandC++Builderの場合、これがAnisString(String) C++でプログラミングをする以上、2.か3.で考えるべきです。 1.も扱えるように勉強したほうが良いのですが1週間の期限内では無理でしょう。 2.3.にせよ1週間で全てをマスターすることは土台無理がありますから、とりあえずはテキストマイニングを実装する上で必要な機能だけを理解するしかないでしょう。 おそらく初心者にとって楽なのは3. しかしC++標準ライブラリの使い方がある程度身についているのなら2.の方が効率的なコードが書けると思います。 この辺はご自分で判断してください。 あとは分からないことがあれば 「このライブラリを使って、このような文字列に対してこのような操作をしたい。どうすればよいか?」 と具体的に質問してください。 文字列操作について1から10まで全てレクチャーするのは回答者側としても不可能です。
お礼
返事が遅くなり申し訳ありません。 かなり前の質問ですが、その後大いに役立ちました。 ありがとうございました。
- Directxq
- ベストアンサー率42% (8/19)
↓下にプログラムを投稿した者です。 文字列の扱いやファイルオープンなど基本的な事も難しいようですので、1から説明していきますね。 投稿は下の投稿から先にお読みください。 #include <stdio.h> #include <string.h> //BUFFER_SIZEはただの定義で書かなくても直接かいてもOK #define BUFFER_SIZE 10000 char string[BUFFER_SIZE]; //検索するための関数です。 void kensaku(void){ //構造体を使っています。もしも構造体についてわからなければ別に聞いてください。 struct s{ char st[100]; int counter; }; struct s string2[100]; int i=0,j=0,n,count=0,h_count=0; char string3[100]; printf("検索終了を示す文章を入力してください。(この文章は検索出来ません)\n"); //検索終了を示すキーワードを格納 scanf("%s",&string3); printf("設定完了。検索終了時には「%s」と入力してください。\n",string3); while(1){ printf("\n*********************************************\n"); printf("検索したい文章は?(終了は「%s」と入力)\n",string3); //検索する言葉を格納 scanf("%s",&string2[h_count].st); //入力された言葉が検索終了のキーワードと同じなら終了 if(strcmp(string2[h_count].st,string3)==0) break; //検索する言葉の文字列の長さを調べる n = strlen(string2[h_count].st); //検索対象が終わりになるまで調べる while(string[i]!='\0'){ //検索対象の中に検索する言葉と同じデータがあれば if(string[i]==string2[h_count].st[j]){ j++; if(j==n) //もしも検索する言葉と同じながさだけデータが一致すれば検索する言葉があったことを示すため カウントする count++; } else{ j=0; } i++; } printf("「%s」の検索ヒット数[%d]回\n\n",string2[h_count].st,count); //検索ヒット数がいくらあったか構造体に格納 string2[h_count].counter=count; //初期化 h_count++; i=0; count=0; } printf("検索終了\n\n"); i=0; //検索結果を一覧表に表示 while(i<h_count){ printf("「%s」\tの検索ヒット数\t[%d]回\n",string2[i].st,string2[i].counter); i++; } return; } void main(){ char string2[BUFFER_SIZE]; FILE *fp; //ファイルオープン fp = fopen("test.txt", "r"); //開けなかったら if ( !fp ) printf("ファイルオープンエラー\n"); //開けたら else{ //1行ずつ読み込み配列に文字列を格納 while( fscanf(fp, "%s" , string2) != EOF){ //読み込んだ文字列を全部一つの配列に連結させる strcat(string,string2); } } //検索する関数を呼ぶ。 kensaku(); fclose(fp); } 以上です。何かわからないことがあれば遠慮なく聞いてください。
- επιστημη(@episteme)
- ベストアンサー率46% (546/1184)
# そのまま提出しても質問攻めを喰らうでしょう /* * 標準入力から得られた文字列から * "美味しい"と"不味い"を検索し * それぞれの個数を出力する */ #include <string> #include <iostream> #include <sstream> int count_str(const std::string& source, const std::string& target) { std::string src(source); int result = 0; std::string::size_type pos; while ( (pos = src.find(target)) != std::string::npos ) { ++result; src.erase(0, pos+target.size()); } return result; } int main() { std::ostringstream ostream; ostream << std::cin.rdbuf(); std::string source = ostream.str(); std::string target; target = "美味しい"; std::cout << "contains " << count_str(source, target) << " of " << target << std::endl; target = "不味い"; std::cout << "contains " << count_str(source, target) << " of " << target << std::endl; }
お礼
お礼が遅くなり、誠に申し訳ありません。 オブジェクト指向の意味すらわからず、理解できませんでした。 今では、include の後に using namespace std; をつければstd:を省略できることもわかるようになりました。 ありがとうございました。
- sekidoutyokka
- ベストアンサー率20% (20/99)
Cが専門なのでC++はあまり分からないのですが、 ヒントになるのであればと思ってアドバイスさせていただきます。 「最初からチェックしていって・・・」というのはできるということなので、それを前提に進めます。 要は最初からチェックしてく方法と同じ事を各変数毎にやればいいのです。 サンプルだと fin>>name>>sintyo;の後に比較すればいいかと思います。 ただ、サンプルのやり方だと掲示板データの様な固定長でないものでも大丈夫なのでしょうか?(前述したとおり、私はC++がわからないのです・・・) 各項目が固定長でないとダメな気がします。 ダメなのなら、「最初からチェックしていって・・・」のやり方でやるか、デリミタ(区切り文字)を読み取って変数に格納して比較・・・とやらなければいけません。 ネットで「C++ ファイル操作」「C++ 文字列比較」で検索すればサンプルが沢山出てくると思います。
お礼
返事が遅くなり申し訳ありません。 本当に”解答”をしていただき、驚きました。 ありがとうございました。