• ベストアンサー

特定の数字でなければエラーを出したい

こんにちは。 Visual Studio 2005を用いてVisual C++を使ったフォームアプリケーションを作成しています。 3つのテキストボックスA、B、Cとボタンがあります。 テキストボックスに数字(半角)を入力させてボタンを押させるのですが、ボタンを押した時に、 Aは0,1,2 Bは0,1,2,3 Cは0,1,2,3,4 のどれかを入力している時のみOKにして、それ以外の文字や数字を入力していた場合はエラーメッセージを出すようにしたいのです。 できるだけ簡単なコードで記述したいのですが、良い方法があればどうぞよろしくお願いします。

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

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

ANo.1の回答のような「KeyPressでどうのこうの」と言う処理は、やってはいけません。 ANo.1の回答では、TABや方向キー、バックスペース、リターンキーを許すように書いていますが、この他に「ペースト」や「カット」も許すべきで、色々な「許さなければならないケース」は予想外に多いのです。 それ以前に「ボタンを押した時に、OKでない入力をされているならエラーを表示する」と言う要求仕様を満たしていません。 プログラムで金を貰ってる人がこんな書き方をしたら「要求仕様を満たしていない。ボツ。作り直せ。作り直しても納期は遅らせるな」と言われます。 プロの場合「別の方法」に、どんなに便利な方法があろうとも「クライアントの要求は絶対」ですから「他の方法」を使うのは許されません。 「ボタンを押したら判定」なのですから、簡単です。 ボタンを押した時に、テキストボックス内の文字列を取得し、その文字列を「文字列変換関数群」の中の「文字列をintに変換する関数」を用いてintに変換しましょう。 「文字列をintに変換する関数」には「変換したintの他に、文字列をどこまで変換したか」も返す関数があります。 「文字列の末尾、つまり、終端文字まで変換した」のなら「文字列すべてを正しく変換した」と判ります。 つまり、第1段階で「intに変換」し、第2段階で「変換出来ない文字列が残ったらエラー」にします。 そして、第3段階で「変換したintの値が、範囲内かどうか?」をif文などで検査します。 例えば、strtoul関数を用いれば "1AA"⇒変換値は1、残った文字列は"AA" "1.1"⇒変換値は1、残った文字列は".1" "CDE"⇒変換値は0、残った文字列は"CDE" "12"⇒変換値は12、残った文字列は無し "1"⇒変換値は1、残った文字列は無し と言う結果が得られます。 "1AA"⇒変換値は1、残った文字列は"AA" "1.1"⇒変換値は1、残った文字列は".1" "CDE"⇒変換値は0、残った文字列は"CDE" は、残った文字列があるので全部エラー。 "12"⇒変換値は12、残った文字列は無し は、残った文字列は無いけど、数値が0,1,2の範囲に無いのでエラー。 "1"⇒変換値は1、残った文字列は無し だけがエラー無しで許可されます。 結論は「strtoul関数を使う」です。

その他の回答 (2)

回答No.3

 こんにちは。  ああ、ボタンを押したと同時に検出でしたか。うっかりしていました。  流石に文章枠外の事に関しては、透視出来ないので、勘弁です。  後、strtoulに関しては、普通では呼べないので要注意です。  どうしても、と言うのならば、#2氏がマーシャリングから一貫したサンプルコードを見せて頂けるそうなので、其方を参考にして下さい。  私はコレで筆を収めさせて頂きます。参考程度に(AとBしかしてませんので念の為)。 //メンバ変数 private:Generic::Dictionary<TextBox^, String^> m_dictionary; //コンストラクタ public: Form1(void) { InitializeComponent(); // //TODO: ここにコンストラクタ コードを追加します // m_dictionary.Add(textBoxA, gcnew String("012")); m_dictionary.Add(textBoxB, gcnew String("0123")); } //受け入れ確認 private: System::Boolean IsAccept(String^% sAccept, const wchar_t wcRhs) { for each(const wchar_t wcLhs in sAccept) if(wcLhs == wcRhs)return true; return false; } //ボタンが押された private: System::Void Button1Click(System::Object^ sender, System::EventArgs^ e) { Generic::Dictionary<TextBox^, String^>::Enumerator it = m_dictionary.GetEnumerator(); while(it.MoveNext()) { TextBox^% textBox = it.Current.Key; String^% sAccept = it.Current.Value; for each(const wchar_t wcPick in textBox->Text) { if(IsAccept(sAccept, wcPick)) continue; //ココで不正扱い MessageBox::Show(this, textBox->Name, "不正を発見"); //テキストボックス1箇所の不正で引き換えしたければbreakを止めてreturnにして下さい break; } } }

回答No.1

 こんばんは。  フォームアプリケーションと書いてありますが、C++/CLIマネージドの事でしょうか。であれば、KeyPressイベントを処理するのが良いと思います。  数字以外にも、TABキーや方向キー、バックスペースキー位は基本で受け付けないと、操作が出来なくなります。  受付可能な数字をarray<int>^にまとめ、textBox^とペアでコレクションクラスの中に突っ込み、KeyPressイベント内で、senderパラメータをキーにしてarray<int>^を取り出して、array<int>^を使用して、総当りで確認して見るなど(こうすれば「0,5,7,8」と言った条件にも耐えられるのでは)。  尚、KeyDownイベント内での処理は、お勧め出来ません。以下参考程度に。 //コレはメンバ変数として追加 private:Generic::Dictionary<TextBox^, array<int>^> m_dictionary; //フォームのコンストラクタ Form1(void) { InitializeComponent(); // //TODO: ここにコンストラクタ コードを追加します // //ココでテキストボックスのインスタンスと、受付可能な数字をまとめた配列を登録する m_dictionary.Add(textBoxA, gcnew array<int>{0, 1, 2}); m_dictionary.Add(textBoxB, gcnew array<int>{0, 1, 2, 3}); } //ココがイベント処理 private:System::Void KeyPress(System::Object^ sender, System::Windows::Forms::KeyPressEventArgs^ e) { //TABや方向キー、バックスペース、リターンキー位は基本で受け入れないとマズい事になる array<wchar_t>^ arDefKeyCode = gcnew array<wchar_t>{37,38,39,40,8,9,13}; //オブジェクトを捕まえる TextBox^ box = dynamic_cast<TextBox^>(sender); //テキストボックスが登録されているかどうか、辞書から探す array<int>^ arFound = m_dictionary.default[box]; //見当たらなかったので引き返す if(arFound == nullptr) return; //基本キーに当てはまったので引き返す for(int i = 0; i < arDefKeyCode->Length; ++i) if(arDefKeyCode[i] == e->KeyChar) return; //10進数である事を確認 if(wchar_t::IsDigit(e->KeyChar)) { //入力文字を数字にする const int iVal = int::Parse(e->KeyChar.ToString()); //条件と合っているかを総当り確認 for(int i = 0; i < arFound->Length; ++i) if(arFound[i] == iVal) return; } //ココまで来れば不正値確定 MessageBox::Show(this, String::Concat(box->Name, " : ", e->KeyChar.ToString()), "不正です"); //テキストボックスへの入力を拒絶する e->Handled = true; }