要件
- 半角英数記号・全角英数記号・全角カタカナ・全角ひらがな・漢字(第一水準・第二水準)を許容して、それ以外はエラー。
- 文字コードはShift-JIS(MS932)。
- 実行環境はJava SE 8u152(たぶんJava8以下でも動くはず。)
色々と試行錯誤したが、とりあえず期待した動作をするようになった。
ソース
簡単に説明すると、
- チェック対象文字列をchar型配列に変換
- いずれかのif~else ifに該当した場合、continueで次文字チェックへ
- 該当しなかった場合、true(エラーあり)を返す。
- 最後の文字までチェックして問題なければ、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