質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

Q&A

解決済

2回答

6503閲覧

金額の入力チェック方法

add-1914

総合スコア16

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

0グッド

2クリップ

投稿2018/01/15 14:49

Java初心者です。
いつも大変お世話になっております。
入力した値[金額]に対して自然数の入力チェックを行いたいのですが、
うまくいかないのでお力を貸してくださいよろしくお願いいたします。

質問は以下の2点です。
・2つ目のソースコードではうまく処理できるのですが、一つ目のソースコードでも同様に行うには
どうしたらよいのでしょうか。
・2つ目のソースコードでは[+100]のような表記ができるようにしたいのですが、
("^[+]|0|^[1-9]\d*|^[1-9]\d{0,2}((,\d{3})*)$")としてもプラスを反映させられなくて困っております。

*また正規表現でいう+の記号は1回以上の繰り返しと認識しているのですが、そもそも+を表現することは可能なのでしょうか。

条件は以下の通りで行っております。
・自然数には0以上の数が入力可能。
・-、小数点が入力されたらエラー
・文字は一文字でも入力されていたらエラー
・カンマの入力は可能。(1,000などの表記は可能)
・数字の前後に空白が入力されていたら取り除く

表示結果がわかりやすいように各System.out.println();でメッセージを出力しています。

###発生している問題・エラーメッセージ
1つめのソースコードで1つでも文字を含んでいたらNGを表示してtrueを返したいが数字を含んだものは通常処理が行われてしまう。

###該当のソースコード

java

1import java.util.Scanner; 2public class Res2 { 3 public static void main (String[] args) { 4 Scanner sc = new Scanner(System.in); 5 System.out.print("金額を入力してください"); 6 String val = sc.next(); 7 8 if (isNotNaturalNumber(val)) { 9 System.out.println("金額には自然数を入力してください"); 10 } else { 11 System.out.println(val); 12 } 13 } 14 15 /** 16 * 自然数のチェック 17 * @param val チェックする値 18 * @return True:エラー、False:正常 19 */ 20 private static boolean isNotNaturalNumber(String val ) { 21 assert val != null; 22 // マイナスおよび小数点は認めない 23 if ((val.indexOf(".") >= 0) 24 || (val.indexOf("-") >= 0)) { 25 System.out.println("指定外です"); 26 return true; 27 28 } 29 // 文字は認めない 30 if (val.matches("(\D)*")) { 31 System.out.println("NG"); 32 return true; 33 } 34 // カンマがあった場合は取り除いてチェックする 35 if (val.indexOf(",") >= 0) { 36 val = val.replace(",", ""); 37 System.out.println("変換します:"+ val); 38 } 39 40 // 空白処理 41 if (val.equals(val.trim())) { 42 System.out.println("空白処理しました"); 43 } 44 return false; 45 } 46} 47

java

1 // 正の整数とカンマ以外は認めない 2 private static boolean isNotNaturalNumber(String val ) { 3 assert val != null; 4    if (!(val.matches("0|^[1-9]\d*|^[1-9]\d{0,2}((,\d{3})*)$"))) { 5 return true; 6 } else { 7 System.out.println("ok"); 8 } 9 // 空白処理 10 if (val.equals(val.trim())) { 11 System.out.println("空白処理しました"); 12 } 13 return false; 14 } 15

###補足情報(言語/FW/ツール等のバージョンなど)
java
環境 eclips Oxygen.1a Release (4.7.1a)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

エラーは適当に出すのではなく、処理できないデータの場合に出しましょう。
入力されたデータは文字列のままで扱うのですか?
数値に直すのではありませんか?

それならばデータを数値に直すメソッドをまず作り、検証にも同じ物を使えばデータの検証と変換で齟齬が生じなくていいと思います。

Java

1public static Integer valueOfCurrency(String s) 2{ 3 Integer result = Integer.valueOf(s.replaceAll("[\s,]", "")); 4 if (result < 0) throw new IllegalArgumentException(); 5 return result; 6}

投稿2018/01/15 23:15

Zuishin

総合スコア28656

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Zuishin

2018/01/15 23:17

これで負の数や小数の場合は例外が生じます。
Zuishin

2018/01/15 23:26

また現在は小数も負の数もはじく設定ですが、将来仕様変更があった時に検証と変換が別々に定義されていたのでは面倒でバグの温床にもなります。 まとめておきましょう。
add-1914

2018/01/16 13:12

Zuishinさんご回答ありがとうございます。 おっしゃる通りあとからLong型またはBigInteger型に直して範囲チェックを行い出力処理を行います。 エラーは本来であれば画面入力の際にエラー表示画面に出力される実装になっているのですが、数字の入力制御のみを行いたかったので割愛しておりました。 早速コードを実行してみたのですがこの方法であれば、文字の入力制御も小数点や負の数の制御も行うことができ、ぎゅっとまとまっていて驚きました。ありがとうございます。 画面からの入力制御といわれて自分の中では正規表現しか考えつかなかったのでとても参考になりました。 ありがとうございます。
guest

0

ベストアンサー

整理すると、下記のような条件になると理解しました。

  • 原則として0-9の数字、および、数字列の右から数えて3桁目ごとに入るコンマのみ許容する。

** ただし、文字列の先頭および末尾にはスペースが存在してもよい。
** ただし、数字の直前には+記号が存在してもよい。

  • こうして受け付けた文字列について、数字以外の文字は無視する。

これを正規表現でまとめるとこんな感じになるでしょう。

^ *[+]*(\d{1,3}(,\d{3})*|\d+) *$

この正規表現に入力がマッチしなかったならばエラーとして撥ねる。
マッチした場合は、もう先頭とか末尾とかごちゃごちゃ考えず、0-9の数字以外すべてを一律に空文字列に置換する。
これで目的が達成できます。

なお+記号ですが、[]の中に置いた場合は通常の文字として扱われます。

質問者コメントを見て追記:
「数字の前の+記号は一文字のみ認める」なら、提示した正規表現の[+]*は[+]?にしてください。

さて、「一般的な処理」かどうかは知りませんが、「間違いなく余計なもの」・「疑いようもなく間違っているもの」は最初に排除しておくほうが何かと楽です。まず「エラー」として突っ返す条件を確定し、これに合わないものはまず弾き、そして受け付けるものについては受け付けた後で余計な文字(カンマ)を削除する作戦がもっともすっきりしていると判断しました。必要なら、部分文字列\1でreplaceすることで、空白を削除した値の出力もこの段階で可能です。私が認識できた範囲ではこれで問題は出ないと考えましたが、なにかうまくいかない部分があるなら補足説明お願いします。

実はjavaは知らないので正規表現の妥当性のみをみて考察しています。「第1のソース」では(\D)*という正規表現で数字でない文字の有無を判断しようとしているようですが、*は「0個以上」の繰り返しですから、存在しないものでもマッチします。javaの正規表現に他の言語で使われているのとは異なる特殊な拡張が施されているのでない限り、これでは用をなさないはずです。仮に「数字以上の文字が含まれる」を正規表現で表すなら\Dだけで充分です。

投稿2018/01/15 17:45

編集2018/01/16 03:43
KojiDoi

総合スコア13669

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

add-1914

2018/01/15 22:20

KojiDoi様ご回答ありがとうございます。 +記号は[]内においたら普通の文字として処理されるのですね、勉強になりました。 ありがとうございます。 説明不足で申し訳ありません。 画面から金額を入力するとしてという前提が抜けておりました。 前提としては以下の4点で行っております。 ・金額には0以上の自然数のみが入力される。 [条件外の文字が入ってきたらエラー] ・最初と最後に空白が入力された場合には値が適切であれば空白を削除した値を出力 ・金額は1,000や1,000,000のようにコンマが入っても可能 ・また画面に+1000や、+10,000のような形で入力された場合も可能とする。(ただし+は左端に1回のみ) また上記のような条件で実装する場合は正規表現のほうが制御しやすいのでしょうか。 カンマ入力がされた場合にはカンマを削除して制御するほうが一般的とお聞きしたのですが、 一番のソースコードで行うとどうしても aaaaはNGを返しますが a0や0ss0などは合致せずうまくいきません。(\D)*(\d)*のようにすると正しい数字まで制御してしまいます。 もしよろしければ教えていただけませんでしょうか・・。
退会済みユーザー

退会済みユーザー

2018/01/15 23:22

` ++123,456,789`が正常でとおらん? 繰り返しが0または1は`?`でないかしら
add-1914

2018/01/16 14:13

KojiDoi様度々のご返答ありがとうございます。 ^ *[+]?(\d{1,3}(,\d{3})*|\d+) *$で行ったところ入力制御を行うことができました。 ありがとうございます。 処理の方法としてどの方法が一番適切なのかがまだわからず質問させていただきました。 条件外を最初に排除して、残った値で必要処理を行う方法が適切なのですね、 ありがとうございます。 第一のソースの件ですが\Dのみですとひとつ文字が入力された場合のみしか合致しなかったため (\D)*で行っておりました。おっしゃるとおり*では0個でも制御されてしまい (\D)+で行いましたがやはり数字がはいった時点で通常処理を行ってしまいます。 やはり正規表現では文字、数字両方を含む場合制限するのは難しいのでしょうか・・
add-1914

2018/01/16 14:42

再度確認してみたところ記載方法ですと+001でも可能になってしまうようでして・・ 自分でも("0|(^[+][1-9]|^[1-9])\d*|((^[+][1-9])|^[1-9])\d{0,2}((,\d{3})*)$")))を用いて実行してみたのですが、正規表現が冗長すぎますでしょうか・・。 動作を確認するにあたってどこまでのテストケースを作成すべきなのかというところにもなるのですが、 ユーザが入力しうる値というのをどこまでを想定すべきのかがわからずあたりそうなケースは全て確認しております。いまいち動作テストを行う際のテストケースをどこまで調べるのかというのもわかっておりません。。
swordone

2018/01/16 15:09 編集

先頭の単独でない0を排除できればいいので、 ^ *[+]?(?!0\d)(\d{1,3}(,\d{3})*|\d+) *$ でどうでしょう。 あと、matchで判定するなら行頭^とか行末$とかいらないですね
add-1914

2018/01/16 15:56

swordone様ご回答ありがとうございます。 正規表現はまだまだ勉強中でしてちょっとわかってないのですが、 (?!0\d)というのは0ではない数字という認識で大丈夫でしょうか。 また^$が必要ないのはmatches()のメソッドが完全一致の場合のみtrueを返すメソッドであるためという認識であってますでしょうか・・・。
swordone

2018/01/16 16:22

(?!...)は先読みと呼ばれるものです。 後に「01」などの「0+数字」が続かない場所にマッチします。 完全一致の件はその通りです。
KojiDoi

2018/01/16 19:39

> 第一のソースの件ですが\Dのみですとひとつ文字が入力された場合のみしか合致しなかったため val.match("正規表現")はvalの「全体」が正規表現にマッチする場合に真となる仕様のようなので、val.matches("(\D)+")は、valが数字以外の文字で終始するときにしか真になりません。 このソースにできるだけ忠実に修正するとすれば、val.match(".*\D.*")でしょうかね。 >再度確認してみたところ記載方法ですと+001でも可能になってしまうようでして これまでのやりとりをみて疑問に思うのですが、第1に、そのようなケースまで余さずすべて撥ねるという方針が本当に必要なのでしょうか? そういう要件なら仕方がありませんが、わたしがもし自由に仕様を決めていいとするならば、+001ぐらいは許容しますが。あまりうるさく撥ね付けるとプログラムが複雑になるだけでなく、ユーザーとしても使いづらくもなりますよね。第2に、すべてのダメなケースにたった一つの正規表現で対応せねばならないというルールがどこかにあるのでしょうか。ややこしく可読性に欠ける正規表現を無理してひねり出すより、いくつかの条件式に分割するとか、and/or演算子を活用するといった方向が無難ではないでしょうか。
add-1914

2018/01/16 22:39

KojiDoi様度々のご回答ありがとうございます。 val.match(”。*\D.*")でも試してみます。本当にありがとうございます。 申し訳ございません。決められた仕様に対してどこまでを許容範囲にしたらいいのかというところがまだわかっておらずすべてのケースを考えておりました。 おっしゃるとおり、ソースコードとしての可読性や保守性も低くなりますし、ユーザーの使い勝手もわるく使いにくいプログラムになりますね・・・ もう少し、条件式や比較演算子をつかって使い勝手のよいものを考えるように意識いたします。 何度もご対応いただき本当にありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問