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

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

ただいまの
回答率

90.98%

  • Java

    12213questions

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

  • 正規表現

    694questions

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

金額の入力チェック方法

解決済

回答 2

投稿

  • 評価
  • クリップ 2
  • VIEW 270

add-1914

score 5

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を返したいが数字を含んだものは通常処理が行われてしまう。

該当のソースコード

import java.util.Scanner;
public class Res2 {
    public static void main (String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("金額を入力してください");
        String val = sc.next();

        if (isNotNaturalNumber(val)) {
            System.out.println("金額には自然数を入力してください");
        } else {
            System.out.println(val);
        }
    }

    /**
     * 自然数のチェック
     * @param val チェックする値
     * @return True:エラー、False:正常
     */
    private static boolean isNotNaturalNumber(String val ) {
        assert val != null;
        // マイナスおよび小数点は認めない
        if ((val.indexOf(".") >= 0)
           || (val.indexOf("-") >= 0)) {
            System.out.println("指定外です");
            return true;

        }
        // 文字は認めない
        if (val.matches("(\\D)*")) {
            System.out.println("NG");
            return true;
        }
        // カンマがあった場合は取り除いてチェックする
        if (val.indexOf(",") >= 0) {
            val = val.replace(",", "");
            System.out.println("変換します:"+ val);
        }

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

補足情報(言語/FW/ツール等のバージョンなど)

java
環境 eclips Oxygen.1a Release (4.7.1a)

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

+2

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

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/01/16 08:17

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

    キャンセル

  • 2018/01/16 08:26

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

    キャンセル

  • 2018/01/16 22:12

    Zuishinさんご回答ありがとうございます。
    おっしゃる通りあとからLong型またはBigInteger型に直して範囲チェックを行い出力処理を行います。
    エラーは本来であれば画面入力の際にエラー表示画面に出力される実装になっているのですが、数字の入力制御のみを行いたかったので割愛しておりました。

    早速コードを実行してみたのですがこの方法であれば、文字の入力制御も小数点や負の数の制御も行うことができ、ぎゅっとまとまっていて驚きました。ありがとうございます。

    画面からの入力制御といわれて自分の中では正規表現しか考えつかなかったのでとても参考になりました。
    ありがとうございます。

    キャンセル

checkベストアンサー

+1

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

  • 原則として0-9の数字、および、数字列の右から数えて3桁目ごとに入るコンマのみ許容する。
    ** ただし、文字列の先頭および末尾にはスペースが存在してもよい。
    ** ただし、数字の直前には+記号が存在してもよい。
  • こうして受け付けた文字列について、数字以外の文字は無視する。

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

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

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

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

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

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

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/01/16 07:20

    KojiDoi様ご回答ありがとうございます。
    +記号は[]内においたら普通の文字として処理されるのですね、勉強になりました。
    ありがとうございます。

    説明不足で申し訳ありません。
    画面から金額を入力するとしてという前提が抜けておりました。
    前提としては以下の4点で行っております。
    ・金額には0以上の自然数のみが入力される。
    [条件外の文字が入ってきたらエラー]
    ・最初と最後に空白が入力された場合には値が適切であれば空白を削除した値を出力
    ・金額は1,000や1,000,000のようにコンマが入っても可能
    ・また画面に+1000や、+10,000のような形で入力された場合も可能とする。(ただし+は左端に1回のみ)

    また上記のような条件で実装する場合は正規表現のほうが制御しやすいのでしょうか。
    カンマ入力がされた場合にはカンマを削除して制御するほうが一般的とお聞きしたのですが、
    一番のソースコードで行うとどうしても
    aaaaはNGを返しますが
    a0や0ss0などは合致せずうまくいきません。(\\D)*(\\d)*のようにすると正しい数字まで制御してしまいます。

    もしよろしければ教えていただけませんでしょうか・・。

    キャンセル

  • 2018/01/16 08:22

    ` ++123,456,789`が正常でとおらん?

    繰り返しが0または1は`?`でないかしら

    キャンセル

  • 2018/01/16 23:13

    KojiDoi様度々のご返答ありがとうございます。
    ^ *[+]?(\d{1,3}(,\d{3})*|\d+) *$で行ったところ入力制御を行うことができました。
    ありがとうございます。
    処理の方法としてどの方法が一番適切なのかがまだわからず質問させていただきました。
    条件外を最初に排除して、残った値で必要処理を行う方法が適切なのですね、
    ありがとうございます。

    第一のソースの件ですが\\Dのみですとひとつ文字が入力された場合のみしか合致しなかったため
    (\\D)*で行っておりました。おっしゃるとおり*では0個でも制御されてしまい
    (\\D)+で行いましたがやはり数字がはいった時点で通常処理を行ってしまいます。
    やはり正規表現では文字、数字両方を含む場合制限するのは難しいのでしょうか・・

    キャンセル

  • 2018/01/16 23:42

    再度確認してみたところ記載方法ですと+001でも可能になってしまうようでして・・

    自分でも("0|(^[+][1-9]|^[1-9])\\d*|((^[+][1-9])|^[1-9])\\d{0,2}((,\\d{3})*)$")))を用いて実行してみたのですが、正規表現が冗長すぎますでしょうか・・。

    動作を確認するにあたってどこまでのテストケースを作成すべきなのかというところにもなるのですが、
    ユーザが入力しうる値というのをどこまでを想定すべきのかがわからずあたりそうなケースは全て確認しております。いまいち動作テストを行う際のテストケースをどこまで調べるのかというのもわかっておりません。。

    キャンセル

  • 2018/01/17 00:09 編集

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

    キャンセル

  • 2018/01/17 00:56

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

    キャンセル

  • 2018/01/17 01:22

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

    キャンセル

  • 2018/01/17 04:39

    > 第一のソースの件ですが\\Dのみですとひとつ文字が入力された場合のみしか合致しなかったため

    val.match("正規表現")はvalの「全体」が正規表現にマッチする場合に真となる仕様のようなので、val.matches("(\\D)+")は、valが数字以外の文字で終始するときにしか真になりません。
    このソースにできるだけ忠実に修正するとすれば、val.match(".*\\D.*")でしょうかね。

    >再度確認してみたところ記載方法ですと+001でも可能になってしまうようでして

    これまでのやりとりをみて疑問に思うのですが、第1に、そのようなケースまで余さずすべて撥ねるという方針が本当に必要なのでしょうか? そういう要件なら仕方がありませんが、わたしがもし自由に仕様を決めていいとするならば、+001ぐらいは許容しますが。あまりうるさく撥ね付けるとプログラムが複雑になるだけでなく、ユーザーとしても使いづらくもなりますよね。第2に、すべてのダメなケースにたった一つの正規表現で対応せねばならないというルールがどこかにあるのでしょうか。ややこしく可読性に欠ける正規表現を無理してひねり出すより、いくつかの条件式に分割するとか、and/or演算子を活用するといった方向が無難ではないでしょうか。

    キャンセル

  • 2018/01/17 07:39

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

    キャンセル

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

  • ただいまの回答率 90.98%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Java

    12213questions

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

  • 正規表現

    694questions

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