10
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Javaで英数記号・ひらがな・カタカナ・JIS第一/第二水準漢字以外をエラーにするチェック処理を実装してみた

Last updated at Posted at 2018-04-23

要件

  • 半角英数記号・全角英数記号・全角カタカナ・全角ひらがな・漢字(第一水準・第二水準)を許容して、それ以外はエラー。
  • 文字コードはShift-JIS(MS932)。
  • 実行環境はJava SE 8u152(たぶんJava8以下でも動くはず。)

色々と試行錯誤したが、とりあえず期待した動作をするようになった。

ソース

簡単に説明すると、

  1. チェック対象文字列をchar型配列に変換
  2. いずれかのif~else ifに該当した場合、continueで次文字チェックへ
  3. 該当しなかった場合、true(エラーあり)を返す。
  4. 最後の文字までチェックして問題なければ、false(エラーなし)を返す。
    /**
     * 第2水準の文字種チェック<br />
     * 半角英数記号・全角英数記号・全角カタカナ・全角ひらがな・漢字(第一水準・第二水準)は許容する。
     *
     * @param str チェック対象文字列
     * @return true:エラーあり/false:エラーなし
     */
    public static boolean checkForbiddenCharacter(String str) {
        char[] charArray = str.toCharArray();
        for (char c : charArray) {
            // JISコードの配列で取得
            int targetChar = getSJISByte(c);

            // 半角文字OK(『?』以外)
            if ((0x20 <= targetChar && targetChar <= 0x3D) || (0x40 <= targetChar && targetChar <= 0x7E)) {
                continue;
            }
            // 半角文字『?』について
            else if (targetChar == 0x3F) {
                // 入力文字が『?』であればOK
                if ("?".equals(String.valueOf(c))) {
                    continue;
                }
                // NG
                else {
                   return true;
                }
            }
            // 半角カナOK
            else if (0xA1 <= targetChar && targetChar <= 0xDF) {
                continue;
            }
            // 全角非漢字OK
            else if (0x8140 <= targetChar && targetChar <= 0x84BE) {
                continue;
            }
            // 第一水準OK
            else if (0x889F <= targetChar && targetChar <= 0x9872) {
                continue;
            }
            // 第二水準OK
            else if ((0x989F <= targetChar && targetChar <= 0x9FFC) || (0xE040 <= targetChar && targetChar <= 0xEAA4)) {
                continue;
            }
            // NG
            else {
                return true;
            }
        }
        return false;
    }
    /**
     * Shift-JISでバイト変換
     *
     * @param c 変換対象文字
     * @return 変換後文字列
     */
    private static int getSJISByte(char c) {
        int targetChar = 0;
        try {
            // MS932でバイト変換
            byte[] byteArray = String.valueOf(c).getBytes("MS932");
            if (byteArray.length <= 1) {
                // 半角文字
                targetChar = (byteArray[0] & 0xFF);
            } else {
                // 全角文字
                targetChar = (((byteArray[0] & 0xFF) * 0x100) + (byteArray[1] & 0xFF));
            }
        } catch (Exception e) {
            ;
        }
        return targetChar;
    }

個人的に良いと思っている点

他のプロジェクトとかに横展開しやすい。
全角カナはエラーにしたいなどと思ったときに、該当するelse~ifを削除するだけで済む。

改善したい点

行数が長い。
もっと行数を短くできるはず。else ifが多すぎ。
すごく長い文字列をチェックすると、1文字ずつ文字チェックしているためレスポンスが悪い。

最後に

こうすればもっと綺麗なコードになる。ここにバグがある。などありましたら誰か教えてください。

2018年5月6日追記

コメントにて修正案を教えていただきました。
注意点ですが、Stream APIを使用しているので、Java 8以降でしか動作しないです。

    /**
     * strが全てcharTypesの文字で構成されているか
     *
     * @param str チェック対象文字列
     * @param charTypes 許容する文字
     * @return true:エラーあり/false:エラーなし
     */
    public static boolean isConsistingWithCharSet(String str, CharType... charTypes) {
        for (char c : str.toCharArray()) {
            // JISコードの配列で取得
            int targetChar = getSJISByte(c);

            // 対象の文字が与えられた文字種でなければfalse
            if(Arrays.stream(charTypes).noneMatch(p -> p.contains(targetChar))) {
                return false;
            }
        }
        return true;
    }

    enum CharType {
        // 半角英数記号
        HALFWIDTH_ALPHANUMERICSYMBOL (p -> ((0x20 <= p && p <= 0x3D) || (0x40 <= p && p <= 0x7E)) || (p == 0x3F && "?".equals(String.valueOf((char)p)))),

        // 半角カタカナ
        HALFWIDTH_KATAKANA (p -> 0xA1 <= p && p <= 0xDF),

        // 全角第一水準漢字
        FULLWIDTH_FIRSTLEVEL_KANJI (p -> 0x889F <= p && p <= 0x9872),

        // 全角第二水準漢字
        FULLWIDTH_SECONDLEVEL_KANJI (p -> (0x989F <= p && p <= 0x9FFC) || (0xE040 <= p && p <= 0xEAA4)),

        // 全角その他
        FULLWIDTH_OTHERS (p -> 0x8140 <= p && p <= 0x84BE);

        private final IntPredicate predicate;

        private CharType(IntPredicate predicate) {
            this.predicate = predicate;
        }

        public boolean contains(int num) {
            return predicate.test(num);
        }
    }
}

enumを使用しているため、引数で指定すれば、半角カタカナのみで構成されているかどうかなどの確認が簡単。使い方はこちら。

    public static void main(String args[]) {
        // true
        System.out.println(isConsistingWithCharSet("あいうえお", CharType.values()));
        // true
        System.out.println(isConsistingWithCharSet("一二三四五", CharType.FULLWIDTH_FIRSTLEVEL_KANJI));
        // false
        System.out.println(isConsistingWithCharSet("abcde", CharType.FULLWIDTH_OTHERS));
    }

また、Unicodeコードポイントのマッピングが一部異なるので、MS932ではなくてShift-JISでコード変換した方が良いとのことでした。

    /**
     * Shift-JISでバイト変換
     *
     * @param c 変換対象文字
     * @return 変換後文字列
     */
    private static int getSJISByte(char c) {
        int targetChar = 0;
        try {
            // MS932でバイト変換
            byte[] byteArray = String.valueOf(c).getBytes("Shift-JIS");
            if (byteArray.length <= 1) {
                // 半角文字
                targetChar = (byteArray[0] & 0xFF);
            } else {
                // 全角文字
                targetChar = (((byteArray[0] & 0xFF) * 0x100) + (byteArray[1] & 0xFF));
            }
        } catch (Exception e) {
            ;
        }
        return targetChar;
    }

かなり行数が短くなりました。コメントくださった方々ありがとうございました。

参考文献

コードに関して、下記のURLを参考にさせていただきました。
http://www.shuiren.org/chuden/teach/code/main4.htm
http://www.asahi-net.or.jp/~ax2s-kmtn/ref/jisx0208.html

10
14
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?