あなたの映画力を試せる!POPLETA映画検定(無料) >>

大学で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を加えるなど、アルゴリズムを考えること自体はできるとは思います。
しかも期限は一週間となっています。
どなたか方法をお教えいただけませんでしょうか。

このQ&Aに関連する最新のQ&A

A 回答 (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インデントなどに置換してからコンパイルしてください。
全角文字があるとエラーになると思うので。

コンパイルできなかった場合や、解らない事があった場合はまた聞いてください。
    • good
    • 0
この回答へのお礼

返事が遅くなり申し訳ありません。
本当に”解答”をしていただき、驚きました。
ありがとうございました。

お礼日時:2007/07/10 13:05

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まで全てレクチャーするのは回答者側としても不可能です。
    • good
    • 0
この回答へのお礼

返事が遅くなり申し訳ありません。
かなり前の質問ですが、その後大いに役立ちました。
ありがとうございました。

お礼日時:2007/07/10 13:07

↓下にプログラムを投稿した者です。


文字列の扱いやファイルオープンなど基本的な事も難しいようですので、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);
}

以上です。何かわからないことがあれば遠慮なく聞いてください。
    • good
    • 0

# そのまま提出しても質問攻めを喰らうでしょう



/*
* 標準入力から得られた文字列から
* "美味しい"と"不味い"を検索し
* それぞれの個数を出力する
*/

#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;
}
    • good
    • 0
この回答へのお礼

お礼が遅くなり、誠に申し訳ありません。
オブジェクト指向の意味すらわからず、理解できませんでした。
今では、include の後に using namespace std; をつければstd:を省略できることもわかるようになりました。
ありがとうございました。

お礼日時:2007/03/17 00:03

Cが専門なのでC++はあまり分からないのですが、


ヒントになるのであればと思ってアドバイスさせていただきます。

「最初からチェックしていって・・・」というのはできるということなので、それを前提に進めます。

要は最初からチェックしてく方法と同じ事を各変数毎にやればいいのです。

サンプルだと
fin>>name>>sintyo;の後に比較すればいいかと思います。

ただ、サンプルのやり方だと掲示板データの様な固定長でないものでも大丈夫なのでしょうか?(前述したとおり、私はC++がわからないのです・・・)
各項目が固定長でないとダメな気がします。

ダメなのなら、「最初からチェックしていって・・・」のやり方でやるか、デリミタ(区切り文字)を読み取って変数に格納して比較・・・とやらなければいけません。

ネットで「C++ ファイル操作」「C++ 文字列比較」で検索すればサンプルが沢山出てくると思います。
    • good
    • 0

このQ&Aに関連する人気のQ&A

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

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

このQ&Aを見た人が検索しているワード

このQ&Aと関連する良く見られている質問

Q文字列中に含まれる文字の個数をカウントするプログラムについて…

文字列、1文字が与えられたとき、これをポインタで入力し文字列中に含まれる文字の個数を計算するプログラムを作成せよ。

と、いう課題がだされたんですけど、ユーザが任意の文字列と1文字を入力できるようにすることができません…。
多分main関数の部分をちょっといじくればよいと思うのですが…。
どなたかアドバイスをお願いします。
#include <stdio.h>

int count(const char *str, const char ch)
{
int cnt=0;
while (*str!='\0')
{
if (*str==ch)
cnt++;
str++;
}
return cnt;
}

int main()
{
const char *str="hello,world!";
const ch='o';

int cnt;

cnt=count(str, ch);

printf("%s中に%cは%d個です\n", str, ch, cnt);


return 0;
}

文字列、1文字が与えられたとき、これをポインタで入力し文字列中に含まれる文字の個数を計算するプログラムを作成せよ。

と、いう課題がだされたんですけど、ユーザが任意の文字列と1文字を入力できるようにすることができません…。
多分main関数の部分をちょっといじくればよいと思うのですが…。
どなたかアドバイスをお願いします。
#include <stdio.h>

int count(const char *str, const char ch)
{
int cnt=0;
while (*str!='\0')
{
if (*str==ch)
...続きを読む

Aベストアンサー

scanf"%s", str)は、バッファオーバーフローの危険と改行文字の食べ残し問題がありますね。
-- 8< -- 8< -- 8< -- 8< -- 8< -- 8< -- 8< -- 8< --
int main()
{
char ch;
char str[BUFSIZ];
int cnt;

printf("文字列を入力して下さい > ");
fgets(str,BUFSIZ,stdin);

printf("カウントする文字を入力してください > ");
ch = getchar();

cnt=count(str, ch);
printf("%s中に%cは%d個です\n", str, ch, cnt);

return 0;
}

Qファイルやディレクトリの存在確認を行う方法

ファイルをオープンするのはfopenでOKですが、ファイルやディレクトリの存在確認を行う方法が知りたいです。

何か組み合わせて作るものなのでしょうか?
perlとか便利な演算子があるのですが、C/C++って器用ではないですね。
これは処理系?依存の内容ですか?

私の環境は VC6, VC2005 Windows2000です。

Aベストアンサー

int access(const char* path, int mode);
int stat(const char* path, struct stat* sb);

かな?
MSDN を引くと _access_s() を使えとか書いてあるけど。

QLNK2019: 未解決の外部シンボルのエラーが出る

Microsoft Visual Studio 2008
Version 9.0.21022.8 RTM
Microsoft .NET Framework
Version 3.5 SP1
----------------------------------------------------------------
新しいプリジェクト→Win32 コンソール アプリケーション(ソリューションのディレクトリを作成 チェック外す)→Windows アプリケーション(空のプロジェクト チェック外す)
----------------------------------------------------------------
 プログラム

 mymain.cpp
#include "myhelper.h"
#include "mymain.h"

//自キャラのデータ
Point2D g_jikipos = {40, 400};//自キャラの座標

//画像ハンドル
int g_jikiimage[11];

//色々なファイルの読み込み
int LoadFiles(){
//画像ファイル読み込み
if(LoadDivGraph("media\\player01.bmp",
11,11,1,64,64,g_jikiimage) == -1) return -1;

return 1;
}


 mymain.h
//他から呼び出させるMyMainの関数
void MyMain();
int LoadFiles();


 myhelper.h(サンプルなので打ちミスはない)
#include "DxLib.h"
#include <limits.h>
#include <math.h>

//構造体宣言
//座標またはベクトルを記録する構造体
struct Vector{
float x,y;
};
typedef Vector Point2D;
//線を記録する構造体
struct Line2D{
Point2D startpos, endpos;
float katamuki;//傾きをラジアン値で記録
Vector speed;//移動している場合は速度をセット
};
//球体を記録する構造体
struct Ball2D{
Point2D position;
float hankei;//半径
};
//四角形を記録する構造体
struct Rect2D{
Point2D lefttop;
Point2D rightbottom;
float width;
float height;
};


//ライブラリ関数
Point2D PosInView(Point2D in);
int XInView(float inx);
int YInView(float iny);
void ScrollToLeft(float jikiposx);
void ScrollToRight(float jikiposx);
void ScrollToUp(float jikiposy);
void ScrollToDown(float jikiposy);
void DrawLineInView(float x1, float y1, float x2, float y2, int Color, int Thickness);
void DrawCircleInView(float x, float y, float r, int Color, int FillFlag);
void DrawAnimation(float x, float y, double ExtRate, double Angle,int TurnFlag,
int *imgarray, int allframe, float fps);
//ベクトル関数
Vector CreateVector(Vector in, float veclen);
Vector AddVector(Vector v1, Vector v2);
Vector SubVector(Vector v1, Vector v2);
Vector AddVectorInFrameTime(Vector pos, Vector speed);
Vector AddVectorInFrameTime2(Vector pos, Vector speed, Vector accel);
Vector Normalize(Vector in);
Vector RotateVector(Vector in, float radian);
float VectorLengthSquare(Vector in);
float DotProduct(Vector v1, Vector v2);
float CrossProduct(Vector v1, Vector v2);
void SetLine2DKatamuki(Line2D *in);
void DrawLine2D(Line2D in, int Color, int Thickness);
void DrawBall2D(Ball2D in, int Color, int Fill);
//当たり判定関数
bool HitTestLineAndBall(Line2D linein, Ball2D ballin);
bool IsPointAtLineFace(Line2D linein, Point2D ptin);
bool HitTestLineAndLine(Line2D line1, Line2D line2);
bool HitTestBallAndBall(Ball2D a, Ball2D b);
bool HitTestPointAndBox(Rect2D rect, Point2D pt);
//タイマー関数
void SetSimpleTimer(int idx, int time);
int GetPassedTime(int idx);


//グローバル変数
extern float g_frametime;
extern Rect2D g_framerect;//画面領域(当たり判定)
extern Point2D g_current_field_pos;//現在の左上座標
extern Rect2D g_stagesize;//ステージサイズ

//定数宣言
const float ZEROVALUE = 1e-10f;
const float PIE = 3.1415926f;
const int SCROLL_LIMIT = 200;
----------------------------------------------------------------
 エラー内容
1>myhelper.obj : error LNK2019: 未解決の外部シンボル "void __cdecl MyMain(void)" (?MyMain@@YAXXZ) が関数 _WinMain@16 で参照されました
1>C:\Documents and Settings\Owner\My Documents\Visual Studio 2008\Projects\my\Debug\my.exe : fatal error LNK1120: 外部参照 1 が未解決です
1>my - エラー 2、警告 0
ビルド: 0 正常終了、1 失敗、0 更新不要、0 スキップ
----------------------------------------------------------------
画像を貼り付けときます
(見えにくい場合→http://www.dotup.org/uploda/www.dotup.org154142.jpg.html)
初心者なのでわかりやすくお願いします

Microsoft Visual Studio 2008
Version 9.0.21022.8 RTM
Microsoft .NET Framework
Version 3.5 SP1
----------------------------------------------------------------
新しいプリジェクト→Win32 コンソール アプリケーション(ソリューションのディレクトリを作成 チェック外す)→Windows アプリケーション(空のプロジェクト チェック外す)
----------------------------------------------------------------
 プログラム

 mymain.cpp
#include "myhelper.h"
#include "mymain.h"

//自...続きを読む

Aベストアンサー

ファイル構成から推測するに
mymain.cpp というファイルに
void MyMain(void) {
// ここに処理を書く
}
という関数が必要なようです。

Qint型からchar型への変換

タイトル通り、int型からchar型への変換の仕方がわかりません!><
どうしたらいいのでしょうか?

Aベストアンサー

#include <stdio.h>


char buf[5];
int no;

no = 10;
sprintf(buf, "%d", no);

Q入力された文字列から整数だけを取り出す方法

入力された文字列から整数だけを取り出すにはどうしたらよいのでしょうか?
例えば、

(1,1,4,4,2,3,2,3)

と入力された時、'(' や ',' は無視して、

1 1 4 4 2 3 2 3

のみを取り出したいのですが。。
自分なりにiostreamの関数について調べてみたのですが、良い方法が思いつきません。
どなたかお力添えをお願いいたします。

Aベストアンサー

LippmanのC++プライマー(第4版)のp.340~p.342に、
8.5 stringストリーム
という節があって、p.342に、

「istringstream を使って format_message(数値と文字列の並びが格納された ostringstream オブジェクト)を読めば数値を取り出すことができる。
文字列表現を数値に自動変換してくれるのである。

istringstream input_istring(format_message.str());
string dump;
input_istring >> dump >> val1 >> dump >> val2;   」

て書いてありました。
input_istring の内容構成について知っていなければならない条件付きですけど。
ちなみにヘッダは sstream です。

QC言語 配列の長さの上限

C言語で配列Array[N]の長さNの上限っていくらなんでしょうか?
もし可能なのであれば上限を2147483647にしたいのですが、方法を教えてください。

Aベストアンサー

そもそもWindowsの32bit版はアプリが仮想メモリ空間を2GBしか使えません。2GBを超えるには64bit版が必要です。
たとえ64bit版OSだとしても添え字が2147483647って、単純なintの配列だとしても4x2147483647=8GB必要ですね。実メモリ16GBとかのPCを用意しますか?
そもそも配列で2147483647個必要なアルゴリズムに問題ありだと思います。

QCStringのFindで文字列検索を行いたいのですが

こんにちは。
いつもお世話になっております。

CStringのFindメソッドで文字列検索を行っているのですが、文字列ではなくコードで検索が行われてしまい困っています。
-------------------------------------------------------

CString TargetStr;

TargetStr = "ハヒフヘホ";
if(TargetStr.Find("z", 1) != -1){
return FALSE;
}

-------------------------------------------------------

上記で文字"ホ"のコードが「837A」で"z"が「7A」のため、
文字列が存在するという結果が帰ってきます。
コードではなく文字そのものの検索をするにはどうしたらよいでしょうか?
基本的なことで申し訳ありませんが、ご教示下さい。

<環境>
Windows 2000, VC6.0

Aベストアンサー

CString::Findメソッドは、1バイトごとの検索を行うので、そのような結果になってしまいます。

このような処理を避けるには、CString::Findではなく、_mbsstrを利用します。
詳しくは、MSDNを参照のこと。
http://msdn.microsoft.com/library/ja/default.asp?url=/library/ja/vclib/html/_crt_strstr.2c_.wcsstr.2c_._mbsstr.asp

どちらかといえば、_tcsstrを利用してUnicodeとMBCSの両方に対応できるようにしておいたほうがいいです。
#この場合、_UNICODEもしくは_MBCSのどちらかが設定されていないと正しく動きません。

*注意*
この関数はANSIに互換性がないようですので、Windows以外では利用できない可能性があります。


また、上記のような関数を使わず、自分で実装するなら、以下の参考URLを参考にしてください。

参考URL:シフトJIS文字列の文字検索

参考URL:http://katsura-kotonoha.sakura.ne.jp/prog/c/tip0002d.shtml

CString::Findメソッドは、1バイトごとの検索を行うので、そのような結果になってしまいます。

このような処理を避けるには、CString::Findではなく、_mbsstrを利用します。
詳しくは、MSDNを参照のこと。
http://msdn.microsoft.com/library/ja/default.asp?url=/library/ja/vclib/html/_crt_strstr.2c_.wcsstr.2c_._mbsstr.asp

どちらかといえば、_tcsstrを利用してUnicodeとMBCSの両方に対応できるようにしておいたほうがいいです。
#この場合、_UNICODEもしくは_MBCSのどちらかが設定されていないと...続きを読む

Qファイルの中身検索と表示

Cでの作成についてなんですが、
fopenでテキストファイルを開き、テキストの中身の
特定文字が含まれている行を新に作ったファイルの中に
書き込みたいんですが、テキストの中身の検索と
抽出方法がいろいろ試してみましたがわかりません。
よろしくお願いします。

Aベストアンサー

★文字列の検索関数を使えば良い。
・テキストの中身の検索は、行単位で文字列を取得(fgets)してから、この文字列内に
 検索文字列の有無をチェックします。この有無は文字列から文字列を検索する関数
 『strstr』関数を使うのが便利です。また、文字列から1文字の検索を行う場合は
 『strchr』関数を使います。質問では、特定の文字が含まれている行となっていますが
・検索するのは1文字、文字群、文字列のどれですか?
 これによっていろいろと代わりますよ。処理が…。
・とりあえず文字列から特定の文字列を含まれた行を抽出するサンプルを下に載せます。

サンプル:
char buff[ 256 ];
char *find = "検索文字列";
FILE *fp; ←読み込み用
FILE *fo; ←抽出用

省略(fopen×2つ)

while ( fgets(buff,sizeof(buff),fp) != NULL ){
 if ( strstr(buff,find) != NULL ){
  fputs( buff, fo );
 }
}

省略(fclose×2つ)

解説:
・上記のサンプルでは、行単位でテキストの中身を取得して、この文字列から検索文字列(find)が
 含まれるか『strstr()』関数でチェックしています。見つかると『NULL』以外を返します。
・そして、見つかったらば行単位で『fputs』関数で出力しています。
 これで見つかった行だけが、新たに作ったファイルに書き込まれていきます。→抽出です。
・また『strstr()』関数の部分を
 『if ( strchr(buff,'a') != NULL ){ … }』とすると『a』の文字が含まれる行のみを抽出します。
 『if ( strspn(buff,"+-*/") != 0 ){ … }』とすると『+-*/』の文字群が含まれる行のみを抽出します。
 検索する文字とは、1文字、文字群、文字列のどれですか?
・以上。おわり。→上記のを参考にして下さい。

参考URL:http://www9.plala.or.jp/sgwr-t/lib/strspn.html,http://www9.plala.or.jp/sgwr-t/c/sec17.html

★文字列の検索関数を使えば良い。
・テキストの中身の検索は、行単位で文字列を取得(fgets)してから、この文字列内に
 検索文字列の有無をチェックします。この有無は文字列から文字列を検索する関数
 『strstr』関数を使うのが便利です。また、文字列から1文字の検索を行う場合は
 『strchr』関数を使います。質問では、特定の文字が含まれている行となっていますが
・検索するのは1文字、文字群、文字列のどれですか?
 これによっていろいろと代わりますよ。処理が…。
・とりあえず文字列から特定...続きを読む

Q単語の出現回数を数え、出現回数順に表示するには?

標準入力または入力ファイルから単語の出現回数を調べるプログラムの一部です。ENTRY *add_entry(ENTRY *new)関数で以前に出てきた単語か(正確には以前に同一内容の行があったかを)判断し、既出ならばvoid do_one(FILE *fp)関数内部でp->count++として出現回数に加算しています。void do_one(FILE *fp)関数の最後の方のprintfで単語とその出現回数を表示します。これだとappleという単語が2回出てくれば、
apple:1
apple:2
と表示されます。この場合は
apple:2
だけ表示させ、さらに、出現回数の多い順に単語を並べて表示させたいんです。どういう風にすればいいでしょうか?

typedef struct entry_t {
char *str;
struct entry_t *next;
int count; /* 単語の出現回数 */
} ENTRY;

int main(int argc, char **argv)
{
FILE *fp;
char *s;
init_hash();
--argc;
++argv;
if (argc == 0)
do_one(stdin);
else {
while (argc--) {
if ((fp = fopen(*argv, "r")) == NULL)
cant(*argv); /* no return */
do_one(fp);
fclose(fp);
argv++;
}
}
free_all_entries();
return (0);
}

void do_one(FILE *fp)
{
ENTRY *p;
char buf[MAX_SIZE];
int i;

while (fgets(buf, MAX_SIZE, fp) != NULL) {
if ((p = malloc(sizeof(ENTRY))) == NULL)
error("can't alloc memory"); /* no return */
if ((p->str = strdup(buf)) == NULL)
error("can't alloc memory"); /* no return */
if (add_entry(p) != p) {
p->count++;/* 登録済みなので単語の出現回数を1増やす */
free(p->str); /* すでに登録されているので開放 */
free(p);
}
else/* 新しく単語を登録する場合 */
p->count =1;/* 単語の出現回数を1にする */
printf("%s : %d\n", p->str, p->count);
}
}

void init_hash(void)
{
int i;

for (i = 0; i < HASHSIZE; i++)
table[i] = NULL;
}

ENTRY *add_entry(ENTRY *new)
{
ENTRY *p;
int h;

h = hash(new->str); /* 追加する単語のハッシュ値を求める */
for (p = table[h]; p != NULL; p = p->next)
if (strcmp(p->str, new->str) == 0) {/* 追加済みの単語ならば */
return (p);/* そのデータのポインタを返す */
}
new->next = table[h];
table[h] = new;/* 単語を追加する */
return (new);
}

標準入力または入力ファイルから単語の出現回数を調べるプログラムの一部です。ENTRY *add_entry(ENTRY *new)関数で以前に出てきた単語か(正確には以前に同一内容の行があったかを)判断し、既出ならばvoid do_one(FILE *fp)関数内部でp->count++として出現回数に加算しています。void do_one(FILE *fp)関数の最後の方のprintfで単語とその出現回数を表示します。これだとappleという単語が2回出てくれば、
apple:1
apple:2
と表示されます。この場合は
apple:2
だけ表示させ、さらに、出現回数の多い順に単...続きを読む

Aベストアンサー

(英単語限定!)

★配列を使ったら「簡単」では・・?
 (短所:オーバーフロー(▼)の都度、要調整)

#define MAXCNT (2000) // ▼
#define un_char unsigned char // 大小比較あるため
int igStoreCnt = 0; // 出現単語カウンタ(グローバル)
typedef struct{
 un_char cStr[16]; // 1単語16文字以内
 int iCount; // 当単語の出現回数
}ENTRY;
ENTRY sStore[MAXCNT]; // 単語収録(▼)

★まず読み込んだ1行を単語毎(◆)に分割
int OneLineWordCount( un_char cBuf[] )
{
 int i, iTop, iLen = 0;

 for( i = 0; i < 2048; i++ ){ // レコード長 2048 以内

  if( 0x09 == cBuf[i] ) cBuf[i] = 0x20; // タブ
  if( 0x0D >= cBuf[i] ){ // 行末

   if( iLen ) WordCount( iTop, cBuf, i ); // ◆

   return( 0 );
  }
  if( 0x27 == cBuf[i] ) continue; // 例) it's の '

  if( ! isalpha( cBuf[i] ) ){ // デリミタ
   if( 0 == iLen ) continue;

   WordCount( iTop, cBuf, i ); // ◆

   iLen = 0;
   continue;
  }
  if( 0 == iLen ) iTop = i;
  iLen++;
 }
 return( 0 );
}

★カウント部分
int WordCount( int iTop, un_char cBuf[], int iii )
{
 int k;

 cBuf[iii] = 0x00;

 for( k = 0; k < igStoreCnt; k++ ){

  if( NULL == strcmp( sStore[k].cStr, &cBuf[iTop] ) ){

   sStore[k].iCount++; // 既存

   return( 0 );
  }
 }
 strncpy( sStore[igStoreCnt].cStr, &cBuf[iTop], 16 );

 sStore[igStoreCnt].iCount = 1; // 新単語

 igStoreCnt++;

 if( MAXCNT < igStoreCnt ) ErrorStop( "Over" ); // ▼

 return( 0 );
}

★出力部分(同数の場合出現順)
void Ranking( void )
{
 int k, j, iMax, iBasyo;

 for( k = 0; k < igStoreCnt; k++ ){

  iMax = 0;

  for( j = 0; j < igStoreCnt; j++ ){

   if( sStore[j].iCount > iMax ){

    iMax = sStore[j].iCount;

    iBasyo = j;
   }
  }
  printf( "%4d ", k );
  printf( "%-16s", sStore[iBasyo].cStr );
  printf( "%3d\n", sStore[iBasyo].iCount );

  sStore[iBasyo].iCount = 0; // 用済み
 }
}
void ErrorStop( un_char *cMess )
{
 fprintf( stderr, "Err %s\n", cMess );

 exit( 0 );
}
注:インデントに全角空白を使用しています。

(英単語限定!)

★配列を使ったら「簡単」では・・?
 (短所:オーバーフロー(▼)の都度、要調整)

#define MAXCNT (2000) // ▼
#define un_char unsigned char // 大小比較あるため
int igStoreCnt = 0; // 出現単語カウンタ(グローバル)
typedef struct{
 un_char cStr[16]; // 1単語16文字以内
 int iCount; // 当単語の出現回数
}ENTRY;
ENTRY sStore[MAXCNT]; // 単語収録(▼)

★まず読み込んだ1行を単語毎(◆)に分割
int OneLineWordCount( un_char cBuf[] )
...続きを読む

Qアプリケーション終了時例外エラー(アクセス違反)の調査方法について

大変困っています。

アプリケーションが終了するときに「アクセス違反」がワトソン博士によって取得されています。
当方アプリケーションなどに弱く、解決策の想像が付きません。どなたかご教授お願いいたします。

<解決策例>
・どういったスキルを持った人にどの様な調査を進めさせれば良いのか。。。
・以前同様な事があり原因は○○だった
・恐らく○○だろう
 など、お願いいたします。

<ユーザ報告>
処理終了し、画面が消えたところでワトソン博士のメッセージが表示された

<ログ抜粋>
例外番号c0000005(アクセス違反)

ファンクション:RtlDestroyHeap
~略~
フォールト → 77f6d672 8908 mov [eax],ecx ds:09000001=00000000

<備考>
開発環境:MSVC6.0
動作環境:Windows NT4.0 SP6a
発生頻度:2回/年
使用頻度:2~3回/(平日)

以上、よろしくお願いします。

Aベストアンサー

発生頻度が年2回というのはなかなか厳しい条件ですね。

さて・・・

「私であれば、次の手順で調査を行います。」という書き始めで延々とデバッグ方法を書いていたのですが、書き終わってからちょっとGoogleで検索したら、ひょっとするとちょうど質問者さんのトラブルと同じかもしれない現象がMicrosoftのKBにありました。

場所はここです: http://support.microsoft.com/kb/168006/ja

要点をかいつまんで書くと、MSVCRT/MFCのDLLのバージョン不整合でエラーが発生することがある、という内容です。KB自体は特定のアプリケーションについて書かれていますが、記述されている現象と原因の関係から考えるに、他のアプリケーションでも同様の現象が発生すると思われます。

さてさて。

せっかく書いた文章を捨てるのがもったいないので(貧乏性)、邪魔かもしれませんが下に続けることにします。もし上のKBの内容がそれらしいようであれば、読み飛ばしてください。

========

私であれば、次の手順で調査を行います。

1. MAPファイル、CODファイル作成

「ワトソン博士のログを取得した際に実行していたEXEファイル」をビルドした際、一緒にMAPファイルやCODファイル(リスティングファイル)を作成していれば、そのファイルを用意しておきます。

もし作成していない場合は、「ワトソン博士のログを取得したEXEファイル」と、バイナリレベルで全く同じEXEファイル(バイト単位で比較すると、ファイルに埋め込まれたタイムスタンプ・チェックサム以外は一致する)が作成可能かどうか調べます。(ビルドに必要なソースファイルやビルドオプションに変更を加えていなければ作成可能です。)

作成可能であれば、コンパイルオプションに「リスティングファイルタイプ:マシン語コードとソースを含む」、リンクオプションに「MAPファイル作成」を追加してEXEを再作成してください。これで、「ワトソン博士のログを取得した際に実行していたEXEファイル」に対応するMAPファイルとCODファイルが得られます。

2. エラー発生行を特定

ワトソン博士のログがどれだけ取れているかにもよりますが、スタックダンプが含まれていればたいていエラー発生行を特定できます。

まず「フォールト->」が含まれる逆アセンブルリストを探します。次に、その下にある「スタックバックトレース」を探します。

スタックバックトレースを上から下に順にたどっていくと、そのうち「ReturnAd」(リターンアドレス)がアプリケーションのアドレス範囲(VC++6の標準オプション設定を変更していなければ0x00400000~)に入るところが出てきます。見つかったら、そのアドレスの直前にあるcall命令が例外を発生させたAPIを直接呼び出している場所です。

さて、仮にリターンアドレスが0x00401234だったとします。そうしたら、次はMAPファイルを見てこのアドレスがどの関数に属しているか探します。ちょうど0x00401234というアドレスは見つからないでしょうけれども、これに近いアドレスは見つかるはずです。そのアドレスに対応する関数名もMAPファイルにあります。

次はその関数名をCODファイルから探します。見つかったら、MAPファイルにあるアドレスがCODファイルにあるマシン語コードの先頭アドレスになるので、そこからリターンアドレス0x00401234に対応するはずの場所まで順番にアドレスを辿っていきます。関数の先頭アドレスが0x00401200であれば、0x34バイト先を探すわけです。

そうすると、その探した場所にある命令の直前の命令がcall命令になっているはずです。CODファイルには、その場所のC++ソースでの行番号とソース文もコメントとして入っているはずなので、あとは対応するソースをよーく見てエラーの見当をつけてください。

アセンブラの知識があれば、そこでcallを使った(他の関数を呼び出した)ときの引数の内容もある程度分かります。(ポインタ渡しだと、そのポインタの先の内容までは分かりませんが。)

3. 置き換え用EXEファイルと対応するMAPファイル作成

これ以降は将来への備えです。

コンパイルオプションでデバッグ情報を「プログラムデータベースを使用」、リスティングファイルタイプを「マシン語コードとソースを含む」、リンクオプションで「MAPファイルを作成する」、デバッグ情報「他の種類」を追加してビルドし、出来たEXEファイルを本番用として使用します。同時に作成されるMAPファイル、CODファイル、PDBファイルは保管しておきます。

MAPファイル、CODファイルの使い方は上記2.のとおりです。PDBファイルは、もし完全なクラッシュダンプが取得できればWinDbgを使って事後ソースレベルデバッグが可能になりデバッグ作業が非常に楽になるので、念のため取っておきます。

4. ワトソン博士のオプション変更

drwtsn32.exeを起動し、「クラッシュダンプファイルの作成」をチェックします。(デフォルトは、チェックが入っています。)

クラッシュダンプファイルとEXEとPDBがあればWinDbgで事後ソースレベルデバッグができます。(いわゆるポストモーテムデバッグです。UNIX系でコアダンプしたコアをデバッガで読み込んでデバッグするのと同じ種類のものです。)


普段何とも思わずに行っていることでも、文章にすると長いですね・・・

えーと、「どういうスキルを持った人に調査させればいいか」については、上記の内容を読んで『なるほど!』と言える人でしょうか。

参考URL:http://support.microsoft.com/kb/168006/ja

発生頻度が年2回というのはなかなか厳しい条件ですね。

さて・・・

「私であれば、次の手順で調査を行います。」という書き始めで延々とデバッグ方法を書いていたのですが、書き終わってからちょっとGoogleで検索したら、ひょっとするとちょうど質問者さんのトラブルと同じかもしれない現象がMicrosoftのKBにありました。

場所はここです: http://support.microsoft.com/kb/168006/ja

要点をかいつまんで書くと、MSVCRT/MFCのDLLのバージョン不整合でエラーが発生することがある、という内容です。KB...続きを読む


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

人気Q&Aランキング