文字数、単語数、行数の計数について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
あや
記事: 28
登録日時: 4年前

文字数、単語数、行数の計数について

#1

投稿記事 by あや » 4年前

入力テキスト中の文字数、単語数、行数を教える問題です.単語は、空白文字で区切られた非空白文字の列とします.
以下が現在のコードです.単語数が正しく出力されません.
コードのどこを直したらよいのか教えてください.よろしくお願いします.

コード:

#include <string>
#include <fstream>
#include <iostream>
using namespace std;

// 定数定義
#define B 0 // 空白文字
#define N 1 // 通常文字

// プロトタイプ宣言
void updateWord(void);  // 単語数を更新する

// 大域変数
int word; // 単語数
int type; // 直前入力文字の種別

/********************************
メイン関数
********************************/
int main() {

	ifstream file;  // 入力ファイル  
	char ch;    // 入力文字
	int letter; // 文字数
	int line; // 行数

	// 1. 文字数、単語数、行数を0とする。
	letter = 0;
	word = 0;
	line = 0;

	// 2. 「直前入力文字の種別」を「空白文字」とする。
	type = B;

	// 3. ファイルを開く。
	file.open("test.txt");

	// 4. ファイルから1文字読み込む。
	ch = file.get();

	// 5. ファイルの未尾に到達するまで以下の処理を繰り返す。 
	while (!file.eof()) {

		// 5-1. 文字数を1増やす。
		letter++;

		// 5-2. 単語数を更新する。
		updateWord();

		// 5-3. 読み込んだ文字が改行文字ならば、行数を1増やす。
		if (ch == '\n') {
			line++;
		}

		// 5-4. ファイルから1文字読み込む。
		ch = file.get();
	}

	// 6. 入力ファイルを閉じる。
	file.close();

	// 7. 文字数、単語数、行数を出力する。
	cout << "文字数:" << letter << endl;
	cout << "単語数:" << word << endl;
	cout << "行数:" << line << endl;
}

/********************************************
単語数を更新する
********************************************/
void updateWord(void) {

	char ch=0;    // 入力文字

	// 1. 読み込んだ文字が空白文字ならば、以下の処理を行う。 
	if (isspace(ch)) {

		// 1-1. 「直前入力文字の種別」が「通常文字」ならば、単語数を1増やす。
		if (type == N) {
			word++;


			// 1-2. 「直前入力文字の種別」を「空白文字」とする。
			type = B;
		}

		// 2. そうでない場合、以下の処理を行う。 
		else {

			// 2-1. 「直前入力文字の種別」を「通常文字」とする。
			type = N;
		}
	}
}

アバター
asd
記事: 319
登録日時: 13年前

Re: 文字数、単語数、行数の計数について

#2

投稿記事 by asd » 4年前

viewtopic.php?f=3&t=20804

以前のトピックでも少し触れていますが、ステップ実行をして単語数を数えている処理で、
・自分が考えている値が各変数に入っているか?
・各変数に入っている値をもとに処理が期待した通りに行われているか?
を確認してみるとよいと思います。
(適当なところでprintf関数を使って変数の値を出力してみる方法でもよいと思います)

入力テキストがどんなふうになっていて、あやさんがどう出力されるのを期待していて、実際はどういう出力になってしまっているのかがわからないので、それを書いておくとよりよいと思います。

また、あやさんがどの程度の知識があるのかわからないのですが、
関数と変数のスコープの関係については理解なされていますか?
(大域変数とのコメントもあるので理解されているものとして回答しています)

おそらくmain関数のchとupdateWord関数内のchが同じものだと思われているものと思いますが、
ステップ実行してupdateWord関数内のchの値を確認してみると期待している通りには動いていないことがわかるかと思います。

そうするとmain関数のchの値(取得した文字)をupdateWord関数内でも参照するにはどうすればいいのか?が
問題解決のカギになるかと思います。
Advanced Supporting Developer
無理やりこじつけ(ぉ

あや
記事: 28
登録日時: 4年前

Re: 文字数、単語数、行数の計数について

#3

投稿記事 by あや » 4年前

以下が入力テキストです.
NASA debuted a color picture from the Spirit rover
on Tuesday showing gray rocks peppering a Martian
lake bed awash in its natural hues of red pink
and orange Mission scientists said they were
bowled over by the spectacular quality of
the image taken with a dual camera system
called pancam that is mounted on a mast jutting
up from the rover I think my reaction has been
one of shock and awe said Jim Bell the team
member in charge of pancam Using special software
mission scientists can fly though the image zooming
in on rocks and other landscape features of interest
It is approximately the color that you would see
with your eyes if you were standing there Bell said
The resolution of course is pretty much what you
would see Pancam has 20 20 vision It is three to
four times better than any previous mission that has gone
to Mars in fact these pictures are the highest resolution
highest detailed pictures of Mars ever obtained They
are absolutely spectacular
単語数:173と出力したいのですが,単語数:0と出力されてしまいます.
main関数のchの値をupdateWord関数内でも参照するにはどうすればいいのか,具体的に教えて下さい.よろしくお願いします.

アバター
asd
記事: 319
登録日時: 13年前

Re: 文字数、単語数、行数の計数について

#4

投稿記事 by asd » 4年前

回答するまえに以前の質問トピックにて解決した際のソース投稿をお願いします。
(解決しましただけではどのように解決したのかわかりませんので)

viewtopic.php?f=3&t=20804
あや さんが書きました:
4年前
以下が入力テキストです.
NASA debuted a color picture from the Spirit rover
on Tuesday showing gray rocks peppering a Martian
lake bed awash in its natural hues of red pink
and orange Mission scientists said they were
bowled over by the spectacular quality of
the image taken with a dual camera system
called pancam that is mounted on a mast jutting
up from the rover I think my reaction has been
one of shock and awe said Jim Bell the team
member in charge of pancam Using special software
mission scientists can fly though the image zooming
in on rocks and other landscape features of interest
It is approximately the color that you would see
with your eyes if you were standing there Bell said
The resolution of course is pretty much what you
would see Pancam has 20 20 vision It is three to
four times better than any previous mission that has gone
to Mars in fact these pictures are the highest resolution
highest detailed pictures of Mars ever obtained They
are absolutely spectacular
単語数:173と出力したいのですが,単語数:0と出力されてしまいます.
main関数のchの値をupdateWord関数内でも参照するにはどうすればいいのか,具体的に教えて下さい.よろしくお願いします.
入力テキストの中身と期待する結果および実際の出力についてご提示いただきありがとうございます。

ただ、私の先の回答ではそれ以外にも確認してほしい内容を提示していますのでそちらの確認もお願いできればと思います。
(それとも、プログラムの動作原理や問題点の洗い出し、それを受けてどうすればよいのかを考えるのはどうでもよくて、体よく答え(あるいは答えに直結する問題点の提示)だけを求めていますか?)

・各変数に入っている値の確認(ステップ実行でも各処理での変数値を標準出力する方法でも構いません)
・関数と変数のスコープの関係を理解しているか?
・main関数内のchとupdateWord関数内のchが同じものであると確認できたか?

あやさんがどこまで知識があるのかわかりませんので参考までに文字読み出し部分と
単語数のカウント関数であるupdateWord関数内でのchについてアドレスと保持している値を出力するように
追記してみました。

コード:

#include <string>
#include <fstream>
#include <iostream>
using namespace std;

// 定数定義
#define B 0 // 空白文字
#define N 1 // 通常文字

// プロトタイプ宣言
void updateWord(void);  // 単語数を更新する

// 大域変数
int word; // 単語数
int type; // 直前入力文字の種別

/********************************
メイン関数
********************************/
int main() {

	ifstream file;  // 入力ファイル
	char ch;    // 入力文字
	int letter; // 文字数
	int line; // 行数

	// 1. 文字数、単語数、行数を0とする。
	letter = 0;
	word = 0;
	line = 0;

	// 2. 「直前入力文字の種別」を「空白文字」とする。
	type = B;

	// 3. ファイルを開く。
	file.open("test.txt");

	// 4. ファイルから1文字読み込む。
	ch = file.get();

	// 5. ファイルの未尾に到達するまで以下の処理を繰り返す。 
	while (!file.eof()) {

		// 5-1. 文字数を1増やす。
		letter++;

		// 5-2. 単語数を更新する。
		cout << "func main ch(" << hex << reinterpret_cast<void *>(&ch) << "): " << ch << endl; // main関数でのch出力
		cout << "func main type(" << hex << reinterpret_cast<void *>(&type) << "): " << type << endl; // main関数でのtype出力
		updateWord();

		// 5-3. 読み込んだ文字が改行文字ならば、行数を1増やす。
		if (ch == '\n') {
			line++;
		}

		// 5-4. ファイルから1文字読み込む。
		ch = file.get();
	}

	// 6. 入力ファイルを閉じる。
	file.close();

	// 7. 文字数、単語数、行数を出力する。
	cout << "文字数:" << letter << endl;
	cout << "単語数:" << word << endl;
	cout << "行数:" << line << endl;
}

/********************************************
単語数を更新する
********************************************/
void updateWord(void) {

	char ch=0;    // 入力文字

	// 1. 読み込んだ文字が空白文字ならば、以下の処理を行う。 
	cout << "func updateWord ch(" << hex << reinterpret_cast<void *>(&ch) << "): " << ch << endl; // updateWord関数でのch出力
	cout << "func updateWord type(" << hex << reinterpret_cast<void *>(&type) << "): " << type << endl; // updateWord関数でのtype出力

	if (isspace(ch)) {

		// 1-1. 「直前入力文字の種別」が「通常文字」ならば、単語数を1増やす。
		if (type == N) {
			word++;


			// 1-2. 「直前入力文字の種別」を「空白文字」とする。
			type = B;
		}
		// 2. そうでない場合、以下の処理を行う。 
		else {

			// 2-1. 「直前入力文字の種別」を「通常文字」とする。
			type = N;
		}
	}
}
実行してみると以下のような結果が出力されるかと思います。
(0x~の部分は値が異なっていると思いますがそれでOKです。)

コード:

func main ch(0x7ffdaaf5663e): N
func main type(0x6021dc): 0
func updateWord ch(0x7ffdaaf56616): 
func updateWord type(0x6021dc): 0
func main ch(0x7ffdaaf5663e): A
func main type(0x6021dc): 0
func updateWord ch(0x7ffdaaf56616): 
func updateWord type(0x6021dc): 0
func main ch(0x7ffdaaf5663e): S
func main type(0x6021dc): 0
func updateWord ch(0x7ffdaaf56616): 
func updateWord type(0x6021dc): 0
func main ch(0x7ffdaaf5663e): A
func main type(0x6021dc): 0
func updateWord ch(0x7ffdaaf56616): 
func updateWord type(0x6021dc): 0
func main ch(0x7ffdaaf5663e):  
func main type(0x6021dc): 0
func updateWord ch(0x7ffdaaf56616): 
func updateWord type(0x6021dc): 0
(以下略)
上記の出力はテキストファイルから1文字読み取るごとにmain関数とupdateWord関数での
読み取り文字を格納している変数chと文字種別を判定した結果を格納している変数typeの出力です。
main関数でのchだけを抜き出してみると、

コード:

func main ch(0x7ffdaaf5663e): N
func main ch(0x7ffdaaf5663e): A
func main ch(0x7ffdaaf5663e): S
func main ch(0x7ffdaaf5663e): A
func main ch(0x7ffdaaf5663e):  
(以下略)
と期待した通りNASA …と1文字ずつ取得されている様子がわかりますよね。
それに対し、単語数カウントを行うupdateWord関数でのchだけを抜き出してみると、

コード:

func updateWord ch(0x7ffdaaf56616): 
func updateWord ch(0x7ffdaaf56616): 
func updateWord ch(0x7ffdaaf56616): 
func updateWord ch(0x7ffdaaf56616): 
func updateWord ch(0x7ffdaaf56616): 
(以下略)
とmain関数で取得できていた値が取れていないことがわかります。
なんで同じchを参照しているはずなのに?と思われるかと思いますが、
その秘密は()内の値にあります。
main関数の方は「0x7ffdaaf5663e」、updateWord関数の方は「0x7ffdaaf56616」となっていますが、
これは変数の値が格納されている場所(アドレス)です。

これが一致していないということは変数の名前は同じでも実際に見ている先は全然別の場所を見ているということになります。

一方、直前の文字種別を保存している変数typeの方は、

コード:

func main type(0x6021dc): 0
func updateWord type(0x6021dc): 0
func main type(0x6021dc): 0
func updateWord type(0x6021dc): 0
func main type(0x6021dc): 0
func updateWord type(0x6021dc): 0
func main type(0x6021dc): 0
func updateWord type(0x6021dc): 0
func main type(0x6021dc): 0
func updateWord type(0x6021dc): 0
(以下略)
のようにmain関数でもupdateWord関数内でも共に「0x6021dc」となっています。
これはなぜかというと変数を宣言している場所の違いによります。

プログラムを見てもらうと変数typeは大域変数としてmain関数の外で宣言されています。
一方変数chはmain関数内とupdateWord関数内でそれぞれ宣言されています。
変数は宣言されたブロック内でしか有効にならないので、main関数内で宣言された変数chは
updateWord関数内では無効になってしまいます。
updateWord関数内でも変数chは宣言されていますが、main関数内の変数chとは別のものとして扱われます。

このあたりの挙動は大域変数と変数のスコープの関係によるものですが、そのあたりはご理解いただけているのでしょうか?

長文になってしまったのでいったんここまでの回答にしますが、変数chを変数typeと同じように大域変数として定義すればmain関数からでもupdateWord関数内からでも参照できる変数になるので、
updateWord関数内でテキストファイルから読み取った文字を参照することは可能になるかと思います。

それ以外にも問題はありますが上記を修正したら再度実行してもらい、変数の値と処理の流れが各所で期待している通りかを確認してもらえればと思います。
Advanced Supporting Developer
無理やりこじつけ(ぉ

アバター
asd
記事: 319
登録日時: 13年前

Re: 文字数、単語数、行数の計数について

#5

投稿記事 by asd » 4年前

あぁ、マルチポストでしたか…。

https://noschool.asia/question/196382-1571002683

サイトのルールはきちんと確認して守ってくださいね。
rules
相互リンクすればマルチポストOK
相互リンクした場合のみ複数の掲示板で同じ質問しても OK

複数の掲示板で同じ質問をすることをマルチポストといい、大抵禁止されています。

しかし、ここでは相互リンクし、リンク先の掲示板でマルチポストが許されていれば

マルチポストはOKとしています。複数の掲示板で同じ質問をするときは相互リンクし、

どこの掲示板で同じ質問をしているか明確にして下さい。
Advanced Supporting Developer
無理やりこじつけ(ぉ

あや
記事: 28
登録日時: 4年前

Re: 文字数、単語数、行数の計数について

#6

投稿記事 by あや » 4年前

以下のコードで実行すると,単語数:94と出力されます.単語数:173と出力するには,どこを直したらいいでしょうか?
正しいコードを教えてください.お願いします.

コード:

#include <string>
#include <fstream>
#include <iostream>
using namespace std;

// 定数定義
#define B 0 // 空白文字
#define N 1 // 通常文字

// プロトタイプ宣言
void updateWord(void);  // 単語数を更新する

// 大域変数
int word; // 単語数
int type; // 直前入力文字の種別
char ch;    // 入力文字

/********************************
メイン関数
********************************/
int main() {

	ifstream file;  // 入力ファイル  
	
	int letter; // 文字数
	int line; // 行数

	// 1. 文字数、単語数、行数を0とする。
	letter = 0;
	word = 0;
	line = 0;

	// 2. 「直前入力文字の種別」を「空白文字」とする。
	type = B;

	// 3. ファイルを開く。
	file.open("test.txt");

	// 4. ファイルから1文字読み込む。
	ch = file.get();

	// 5. ファイルの未尾に到達するまで以下の処理を繰り返す。 
	while (!file.eof()) {

		// 5-1. 文字数を1増やす。
		letter++;

		// 5-2. 単語数を更新する。
		updateWord();

		// 5-3. 読み込んだ文字が改行文字ならば、行数を1増やす。
		if (ch == '\n') {
			line++;
		}

		// 5-4. ファイルから1文字読み込む。
		ch = file.get();
	}

	// 6. 入力ファイルを閉じる。
	file.close();

	// 7. 文字数、単語数、行数を出力する。
	cout << "文字数:" << letter << endl;
	cout << "単語数:" << word << endl;
	cout << "行数:" << line << endl;
}

/********************************************
単語数を更新する
********************************************/
void updateWord(void) {

	

	// 1. 読み込んだ文字が空白文字ならば、以下の処理を行う。 
	if (isspace(ch)) {

		// 1-1. 「直前入力文字の種別」が「通常文字」ならば、単語数を1増やす。
		if (type == N) {
			word++;


			// 1-2. 「直前入力文字の種別」を「空白文字」とする。
			type = B;
		}

		// 2. そうでない場合、以下の処理を行う。 
		else {

			// 2-1. 「直前入力文字の種別」を「通常文字」とする。
			type = N;
		}
	}
}
 

アバター
asd
記事: 319
登録日時: 13年前

Re: 文字数、単語数、行数の計数について

#7

投稿記事 by asd » 4年前

申し訳ありませんが、こちらの問いには答えていただけないようですので、
私は回答を断念します。
大域変数として定義したあとに問題点を探す際にすべきことはすでに回答しています。
直す場所さえわかればそれでいいという丸投げをなさるのであればほかの回答をお待ちください。
頑張ってください。

ルールから引用
rules#board-rule-4-4
d.義務行為

"C言語何でも質問掲示板"でのみ適用される事項

・トピックを立て、解決した場合は「解決しました」とだけ書かず、どうやって解決したか他の人に分かるように書いて  からトピックを終了して下さい。
・複数の回答者がいた場合、都合の良い、または自分の気が向いた回答者にだけ返信を書かず、回答をくれた人 全員に対して出来る限りの返信を書きましょう。
・回答者のコメントの中に複数質問があった場合、出来る限りその全てに答えるようにしましょう。
Advanced Supporting Developer
無理やりこじつけ(ぉ

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 文字数、単語数、行数の計数について

#8

投稿記事 by みけCAT » 4年前

#6のupdateWord関数は、このような処理になっていますね。
20806-updateWord-matigai-20191015.png
現状のフローチャート
20806-updateWord-matigai-20191015.png (10.21 KiB) 閲覧数: 12836 回
例えばこのような処理にすると改善するかもしれません。
20806-updateWord-kaizen-20191015.png
改善案のフローチャート
20806-updateWord-kaizen-20191015.png (9.78 KiB) 閲覧数: 12836 回
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

okubyou

Re: 文字数、単語数、行数の計数について

#9

投稿記事 by okubyou » 4年前

みけcat氏のフローチャートが修正点の核心なので蛇足です。
フローチャートで問題点が理解でき、解決したなら無視してください。

問題点推理の流れの一例として書きます。

現状、単語数:173を期待しているのに、94と出力されます。
少ないです。つまり単語をカウントする部分である word++; が呼ばれる回数が足りないわけです。
word++; は空白文字が連続した際に単語とカウントしないように、if文でかこわれています。
この処理が怪しくなってきます。(期待した動作をしていないかもしれない)

(以下、test.txtの中身がNASAなんたらの英文である前提で書きます)
ここでためしに、updateWord関数の word++; を直前のif文からくくりだしてみます。

コード:

void updateWord(void) {

	// 1. 読み込んだ文字が空白文字ならば、以下の処理を行う。 
	if (isspace(ch)) {
		word++//いったん下のif文から出す (空白文字がくるたび、すべて単語とカウントしてしまう)
	
		// 1-1. 「直前入力文字の種別」が「通常文字」ならば、単語数を1増やす。
		if (type == N) {
			//word++; //いったんコメントアウト
			// 1-2. 「直前入力文字の種別」を「空白文字」とする。
			type = B;
		}
		// 2. そうでない場合、以下の処理を行う。 
		else {

			// 2-1. 「直前入力文字の種別」を「通常文字」とする。
			type = N;
		}
	}
}
余裕があれば試してみてほしいですが、期待した結果にちかいものが得られるはずです。
(幸いNASAなんたらの英文には空白が連続するような部分がないためです)
これで連続した空白文字を単語としてカウントしないようにする部分が、期待した動作をしていないことがほぼ確定しました。

問題のif文はtypeで分岐を行っているので、ここを確認してみましょう。
次は updateWord関数を実験のため以下のように書き換えます。

コード:

void updateWord(void) {

	// 1. 読み込んだ文字が空白文字ならば、以下の処理を行う。 
	if (isspace(ch)) {
		cout << "type: "<< type << endl;//typeの中身を空白文字が来るたびに表示します。
		// 1-1. 「直前入力文字の種別」が「通常文字」ならば、単語数を1増やす。
		if (type == N) {
			word++;
			// 1-2. 「直前入力文字の種別」を「空白文字」とする。
			type = B;
		}
		else {
			// 2-1. 「直前入力文字の種別」を「通常文字」とする。
			type = N;
		}
	} 
}
もとのコードに

コード:

cout << "type: "<< type << endl;//typeの中身を空白文字が来るたびに表示します。
が追加されただけです。
空白文字が読み込まれるたびに、typeがBなら0、Nなら1が表示されます。

コード:

#define B 0 // 空白文字
#define N 1 // 通常文字
これも余裕があればやってみてほしいです。
test.txtには空白文字が連続するような部分はないので 1 だけが表示されてほしいのですが、実行すると0と1が交互に表示されます。原因のにおいがします。


test.txtに空白文字の連続する部分はないのに0が表示される(typeがBになっている)ということは、

コード:

type = N;
が呼ばれるべきタイミングで呼ばれていない(type = N;があるべき場所にない)ということです。
そして先ほどのコードを実行すると交互に出現する0と1。
これはちょっとわかりにくいですが、ここから「空白文字が読み込まれること」と、「通常文字が読み込まれたことの設定(type = N;)」が関係があることが想像できます。
これはおかしいです。「空白文字が読み込まれていないこと」(=通常文字が読み込まれた)と、「通常文字が読み込まれたことの設定(type = N;)」が関係するべきです。

つまり、type = N;があるべき場所に無くて、なくてもいい場所にあるということです。
そしてこの長々とした不必要に冗長な長文の内容を、絵1枚で示しているのが上のフローチャートです。

返信

“C言語何でも質問掲示板” へ戻る