Edited at

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

More than 1 year has passed since last update.


要件


  • 半角英数記号・全角英数記号・全角カタカナ・全角ひらがな・漢字(第一水準・第二水準)を許容して、それ以外はエラー。

  • 文字コードは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