チャットプログラムのバグ

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

チャットプログラムのバグ

#1

投稿記事 by fabersid » 6年前

[1] 質問文
 [1.1] 作っているもの:チャットプログラム(ただしソケットを使わない)
 [1.2] どのように取り組んだか(後述)
 [1.3] ファイル内容を表示するときにごみデータが表示される

[2] 環境  
 [2.1] OS:Windows
 [2.2] コンパイラ名:Borand C++

バグった時の画面
再現性は少ない
display_file関数にバグがあることは確かです。

コード:

ゲストさんが入室しました。
ゲスト>>Hello,C++
ゲスト>>This is bug.
・ゲスト>>
4行目の1文字目に「・」が入り込んでいる

コード:

#define WIN32_LEAN_AND_MEAN		// Windows ヘッダーから使用されていない部分を除外します。
#define LOG_FILE ".dll"
#define KEY 0
#include <windows.h>
#include <string>		//ヘッダファイルインクルード
#include <iostream>		//標準入出力(C++)
#include <fstream>		//ファイル入出力
#include <stdlib.h>		//標準ライブラリ…system()
#include <algorithm>

using namespace std;	//名前空間指定

void add_file(const string);
void display_file();
void history_reset();
string ango(const string);

int main(){
	string user="ゲスト";		//ローカル変数として、str を生成
	string text="";
	string ango;
	
	cerr << "ユーザー名を入力:";
	cin >> user;
	add_file(user + "さんが入室しました。");
	while("EXIT"!=text){
		if(text!=""){
			add_file(user+">>"+text);
		}
		display_file();
		cerr << user << ">>";
		history_reset();
		getline(cin, text);
	}
	add_file(user + "さんが退室しました。");
}

void add_file(const string a){
	if(a=="")return;
	ofstream ofs(LOG_FILE,ios::binary|ios::app);
	ofs << ango(a + "\r\n");
	ofs.close();
}

void display_file(){
	system("cls");
	ifstream ifs(LOG_FILE,ios::binary);
	if(!ifs){ return; } //ファイルが開けたか確認
	char buf[256]; //256バイト分のバッファ
	while(!ifs.eof()){
		ifs.read(buf,256); //ストリームから256バイトバッファに読み込む
		cout << ango(buf); //標準出力に出力してみる
	}
	ifs.close();
}

void history_reset(){
	#define VK_MENU 0x12/*Alt*/
	#define VK_F7   0x76/*F7*/
	// キーの押し下げをシミュレートする。
	keybd_event( VK_MENU, 0, 0, 0);
	// キーの押し下げをシミュレートする。
	keybd_event( VK_F7, 0, 0, 0);
	system("timeout /t 1 /NOBREAK>nul");
	// キーの解放をシミュレートする。
	keybd_event( VK_F7, 0, KEYEVENTF_KEYUP, 0);
	// キーの解放をシミュレートする。
	keybd_event( VK_MENU, 0, KEYEVENTF_KEYUP, 0);
}

string ango(const string tmp){
	string ret=tmp;
	for(int i = 0; i < (int)ret.size(); ++i){
		ret[i] = ret[i] ^ KEY;
	}
	return ret;
}
オフトピック
XOR暗号を使う理由はファイルを改ざんされにくくするためです。

かずま

Re: チャットプログラムのバグ

#2

投稿記事 by かずま » 6年前

ifs.read で常に 256バイト読めるわけではありません。
読む前に buf を初期化しましょう。

コード:

void display_file(){
	system("cls");
	ifstream ifs(LOG_FILE,ios::binary);
	if(!ifs){ return; } //ファイルが開けたか確認
	while(!ifs.eof()){
		char buf[256] = ""; //256バイト分のバッファ
		ifs.read(buf,256); //ストリームから256バイトバッファに読み込む
		cout << ango(buf); //標準出力に出力してみる
	}
	ifs.close();
}

かずま

Re: チャットプログラムのバグ

#3

投稿記事 by かずま » 6年前

256バイト読めたときにも '\0' の分が必要です。

コード:

		char buf[257] = ""; //256バイトと '\0' のバッファ
		ifs.read(buf,256); //ストリームから256バイトバッファに読み込む

かずま

Re: チャットプログラムのバグ

#4

投稿記事 by かずま » 6年前

毎回、buf全体を初期化するということを避けるやり方もあります。

コード:

	char buf[256+1]; //256バイトと '\0 'の分のバッファ
	while(!ifs.eof()){
		ifs.read(buf,256); //ストリームから256バイトバッファに読み込む
		buf[ifs.gcount()] = '\0';
		cout << ango(buf); //標準出力に出力してみる
	}
ところで、history_reset() は何のためにあるのでしょうか?
プロンプトの ">>" が表示された後、1秒間キー入力を受け
付けないのは不便ではありませんか?

通りすがりの......

Re: チャットプログラムのバグ

#5

投稿記事 by 通りすがりの...... » 6年前

history_reset関数はfabersidさんの意図した結果をもたらしません。
cmd.exeで実行するうえで連投防止をしようとしているようですが、
cmd.exeがうまくキーコードを受け取らない時があります。
できるならctrlキーの制御もしたほうがいいかと。
右クリックで貼り付けができるcmdもあるらしいです。
私には技術が足りません
このサイトのコーダーさんに私からもお願いします。

fabersid
記事: 42
登録日時: 6年前

Re: チャットプログラムのバグ

#6

投稿記事 by fabersid » 6年前

追加で実現したい機能
  • history_reset関数を実現する
  • 連投防止
  • 荒らし追放用機能追加
  • 人数表示(system関数で実現)
発生中のエラー及び警告

コード:

エラー E2285 text._ver.2.cpp 126: 'string::operator =(char *)' に一致するものが見つからない(関数 checkFileExistence(const string) )
エラー E2379 text._ver.2.cpp 126: ステートメントにセミコロン(;)がない(関数 checkFileExistence(const string) )
エラー E2285 text._ver.2.cpp 127: 'ifstream::basic_ifstream(const string)' に一致するものが見つからない(関数 checkFileExistence(const string) )
警告  W8057 text._ver.2.cpp 129: パラメータ 'str' は一度も使用されない(関数 checkFileExistence(const string) )
エラー E2285 text._ver.2.cpp 136: 'ofstream::basic_ofstream(const string)' に一致するものが見つからない(関数 user_make(const string) )
警告  W8004 text._ver.2.cpp 138: 'ofs' に代入した値は使われていない(関数 user_make(const string) )
現在のコード(バグ未修正・#2~#5既読)

コード:

#define WIN32_LEAN_AND_MEAN		// Windows ヘッダーから使用されていない部分を除外します。
#define LOG_DIR "DATA.{BB06C0E4-D293-4f75-8A90-CB05B6477EEE}"
#define LOG_FILE LOG_DIR"\\LOG.dll"
//define DATA FILEs
/*
DATA.{BB06C0E4-D293-4f75-8A90-CB05B6477EEE}
  LOG.dll
  Users
    username.dll
    ・
    ・
    ・
*/
#define KEY 123
#include <windows.h>
#include <sys\stat.h>	//フォルダの確認(stat)
#include <direct.h>		//フォルダの作成(_mkdir)
#include <string>		//ヘッダファイルインクルード
#include <iostream>		//標準入出力(C++)
#include <fstream>		//ファイル入出力
#include <stdio.h>
#include <stdlib.h>		//標準ライブラリ…system()
#include <algorithm>

using namespace std;	//名前空間指定

bool user_make(const string);
bool command(const string);
bool checkFileExistence(const string);
void add_file(const string);
void display_file();
void history_reset();
string ango(const string);

int main(){
	string text="";
	string ango;
	string user;

	do{
		user="";
		cerr << "ユーザー名を入力:";
		cin >> user;
	}while(!user_make(user));

	struct stat statBuf;
	if (stat(LOG_DIR, &statBuf) != 0){
		if (_mkdir(LOG_DIR) != 0){
			printf("ディレクトリを作成できませんでした。\n。");
			printf("異常終了\n");
			return 1;
		}
	}
	if (stat(LOG_DIR"\\Users", &statBuf) != 0){
		if (_mkdir(LOG_DIR"\\Users") != 0){
			printf("ディレクトリを作成できませんでした。\n。");
			printf("異常終了\n");
			return 1;
		}
	}
	
	cout << "退室するときはEXITと打ってください。";
	add_file(user + "さんが入室しました。");
	while("EXIT"!=text){
		if(text!=""){
			if(!command(text)){
				add_file(user+">>"+text);
			}
		}
		display_file();
		cerr << user << ">>";
		history_reset();
		getline(cin, text);
		history_reset();
	}
	add_file(user + "さんが退室しました。");
	return 0;
}

void add_file(const string a){
	if(a=="")return;
	ofstream ofs(LOG_FILE,ios::binary|ios::app);
	ofs << ango(a + "\r\n");
	ofs.close();
}

void display_file(){
	system("cls");
	ifstream ifs(LOG_FILE,ios::binary);
	if(!ifs){ return; } //ファイルが開けたか確認
	while(!ifs.eof()){
		char buf[257]=""; //256バイト分のバッファ
		ifs.read(buf,256); //ストリームから256バイトバッファに読み込む
		cout << ango(buf); //標準出力に出力してみる
	}
	ifs.close();
}

void history_reset(){
	#define VK_MENU 0x12/*Alt*/
	#define VK_F7   0x76/*F7*/
	// キーの押し下げをシミュレートする。
	keybd_event( VK_MENU, 0, 0, 0);
	// キーの押し下げをシミュレートする。
	keybd_event( VK_F7, 0, 0, 0);
	system("timeout /t 1 /NOBREAK>nul");
	// キーの解放をシミュレートする。
	keybd_event( VK_F7, 0, KEYEVENTF_KEYUP, 0);
	// キーの解放をシミュレートする。
	keybd_event( VK_MENU, 0, KEYEVENTF_KEYUP, 0);
}

string ango(const string tmp){
	string ret=tmp;
	for(int i = 0; i < (int)ret.size(); ++i){
		ret[i] = ret[i] ^ KEY;
	}
	return ret;
}

//https://qiita.com/takahiro_itazuri/items/e999ae24ab34b2756b04
#include<string>
#include<fstream>
#include<iostream>
bool checkFileExistence(string str){
	str=LOG_DIR"\\Users\\"s+str;
	ifstream ifs(str);
	return ifs.is_open();
}

bool user_make(const string userName){
	if(checkFileExistence(userName)){
		cout << userName << "は使用済みです。";
		return false;
	}
	ofstream ofs(userName);
	return true;
}
bool user_delete(const string userName){
	if(remove((LOG_DIR"\\Users\\"+userName).c_str()) != 0 ){
		fprintf(stderr,"%sは存在しません",userName.c_str());
		return false;
	}
	return true;
}
bool command(const string text){
	#define cmd_exec(x,y) {if(text.substr(0,strlen(x))==x){y(text.substr(strlen(x)));return true;}}
	//x=string y=function
		cmd_exec("/ban ",user_delete);
	#undef cmd_exec
	return false;
}

かずま

Re: チャットプログラムのバグ

#7

投稿記事 by かずま » 6年前

コード:

29行目: bool checkFileExistence(string);    // const を削除
126行目:    str=LOG_DIR"\\Users\\"+str;     // + の前の s を削除
127行目:    ifstream ifs(str.c_str());      // .c_str() を追加
136行目:    ofstream ofs(userName.c_str()); // .c_str() を追加
関数の引数は値渡しなので、const string& や const string* なら
「呼び出し元の文字列を変更しない」という重要な意味が const に
ありますが、const string では無意味です。

ifstream や ofstream のコンストラクタの第1引数に
string が使えるようになったのは C++11からです。

fabersid
記事: 42
登録日時: 6年前

Re: チャットプログラムのバグ

#8

投稿記事 by fabersid » 6年前

 ! メッセージ from: fabersid
history_reset関数の不具合が未解決です。
参考:コマンドプロントの閉じるボタンの無効化のやり方 by aberu » 5年前
   無題 by 恐 » 8年前
#7の既読後コードを追加し強制終了されることによる
不具合が起きにくくなりました。

コード:

#define WIN32_LEAN_AND_MEAN		// Windows ヘッダーから使用されていない部分を除外します。
#define LOG_DIR "DATA.{BB06C0E4-D293-4f75-8A90-CB05B6477EEE}"
#define LOG_FILE LOG_DIR"\\LOG.dll"
//define DATA FILEs
/*
DATA.{BB06C0E4-D293-4f75-8A90-CB05B6477EEE}
  LOG.dll
  Users
    username
    ・
    ・
    ・
*/
#define KEY 123
#include <windows.h>
#include <sys\stat.h>  	//フォルダの確認(stat)
#include <direct.h>		//フォルダの作成(_mkdir)
#include <string>		//ヘッダファイルインクルード
#include <iostream>		//標準入出力(C++)
#include <fstream>		//ファイル入出力
#include <stdio.h>
#include <stdlib.h>		//標準ライブラリ…system()
#include <algorithm>

using namespace std;	//名前空間指定

bool user_make(const string);
bool command(const string);
bool checkFileExistence(string);
void add_file(const string);
void display_file();
void history_reset();
string ango(const string);

int main(){
	string text="";
	string ango;
	string user;

	do{
		user="";
		cerr << "ユーザー名を入力:";
		cin >> user;
	}while(!user_make(user));
	
	struct stat statBuf;
	if (stat(LOG_DIR, &statBuf) != 0){
		if (_mkdir(LOG_DIR) != 0){
			printf("ディレクトリを作成できませんでした。\n。");
			printf("異常終了\n");
			return 1;
		}
	}
	if (stat(LOG_DIR"\\Users", &statBuf) != 0){
		if (_mkdir(LOG_DIR"\\Users") != 0){
			printf("ディレクトリを作成できませんでした。\n。");
			printf("異常終了\n");
			return 1;
		}
	}
	
	const char * consoleName = "退室するときはEXITと打ってください。";
	cout << consoleName;
	Sleep(3000);
	SetConsoleTitle(consoleName);
	Sleep(50);
	HWND hWnd = FindWindowA(NULL, consoleName);
	if(hWnd){
		HMENU hMenu = GetSystemMenu(hWnd, FALSE);
		RemoveMenu(hMenu, SC_CLOSE, MF_BYCOMMAND);
		SetConsoleCtrlHandler(NULL , TRUE);
	}else{
		// printf("%s\n", "失敗");
	}
	
	add_file(user + "さんが入室しました。");
	while("EXIT"!=text){
		if(text!=""){
			if(!command(text)){
				add_file(user+">>"+text);
			}
		}
		display_file();
		cerr << user << ">>";
		history_reset();
		getline(cin, text);
		history_reset();
	}
	add_file(user + "さんが退室しました。");
	return 0;
}

void add_file(string a){
	if(a=="")return;
	ofstream ofs(LOG_FILE,ios::binary|ios::app);
	ofs << ango(a + "\r\n");
	ofs.close();
}

void display_file(){
	system("cls");
	ifstream ifs(LOG_FILE,ios::binary);
	if(!ifs){ return; } //ファイルが開けたか確認
	char buf[256+1]; //256バイトと '\0 'の分のバッファ
	while(!ifs.eof()){
		ifs.read(buf,256); //ストリームから256バイトバッファに読み込む
		buf[ifs.gcount()] = '\0';
		cout << ango(buf); //標準出力に出力してみる
	}
	ifs.close();
}

void history_reset(){/*
        ここでAlt + F7 を押し自動で入力内容の
        履歴を消すはずだが動作しないのでコメント化している。
        ここの部分は検索中
        知っている方は教えていただきたい
	#define VK_MENU 0x12/*Alt*/
	#define VK_F7   0x76/*F7*/
	// キーの押し下げをシミュレートする。
	keybd_event( VK_MENU, 0, 0, 0);
	// キーの押し下げをシミュレートする。
	keybd_event( VK_F7, 0, 0, 0);
	system("timeout /t 1 /NOBREAK>nul");
	// キーの解放をシミュレートする。
	keybd_event( VK_F7, 0, KEYEVENTF_KEYUP, 0);
	// キーの解放をシミュレートする。
	keybd_event( VK_MENU, 0, KEYEVENTF_KEYUP, 0);*/
}

string ango(string tmp){
	string ret=tmp;
	for(int i = 0; i < (int)ret.size(); ++i){
		ret[i] = ret[i] ^ KEY;
	}
	return ret;
}

//https://qiita.com/takahiro_itazuri/items/e999ae24ab34b2756b04
#include<string>
#include<fstream>
#include<iostream>
bool checkFileExistence(string str){
	str=LOG_DIR"\\Users\\"+str;
	ifstream ifs(str.c_str());
	return ifs.is_open();
}

bool user_make(string userName){
	if(checkFileExistence(userName)){
		cout << userName << "は使用済みです。";
		return false;
	}
	string str=LOG_DIR"\\Users\\"+userName;
	ofstream ofs(str.c_str());
	return true;
}
bool user_delete(string userName){
	if(remove((LOG_DIR"\\Users\\"+userName).c_str()) != 0 ){
		fprintf(stderr,"%sは存在しません",userName.c_str());
		return false;
	}
	return true;
}
bool command(string text){
	#define cmd_exec(x,y) {if(text.substr(0,strlen(x))==x){y(text.substr(strlen(x)));return true;}}
	//x=string y=function
		cmd_exec("/ban ",user_delete);
		cmd_exec("/help ",help);
	#undef cmd_exec
	return false;
}
void help(string dummy){
	cout 
		<< "/ban ユーザー名"
		<< "ユーザーをtext.exeから追放します。"
		<< "/help"
		<< "この画面が表示されます。"
		<< "EXIT"
		<< "投稿を終了します。";
}

返信

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