ゲームのユーザー名のチェック処理として適切な正規表現を考えてみる。
結論、これで事足りると思う。
Pattern.compile("[^0-9a-zA-Zぁ-んァ-ヶ一-龠々ー]");
文字の種類
考慮すべき文字種の多すぎてまとめきれる気がしない。
- 全角ひらがな "あいうえお"
- 全角カタカナ "アイウエオ"
- 半角カタカナ "アイウエオ"
- 全角数字 "12345"
- 半角数字 "12345"
- 全角英字大文字 "ABCDE"
- 全角英字小文字 "abcde"
- 半角英字大文字 "ABCDE"
- 半角英字小文字 "abcde"
- 全角記号 "!#$%&"
- 半角記号 "!#$%&"
- 漢字 "亜異兎絵尾"
- 長音 "ー"
- 中点 "・"
- 繰り返し記号 "〃ゝゞヽヾ"
- 機種依存文字 "①②③④⑤"
- キリル文字大文字 "АБВГҐ"
- キリル文字小文字 "абвгґ"
- ハングル "아가다오쓰"
- アラビア "غة العرب"
仕様の決め方
UI上の良し悪し(特に視認性)や、ゲームの世界観を判断材料にして決める。
- 顔文字はNGにしたい (・∀・)
- 括弧をNGにすればよさそう
- ユーザー名に句点・読点はなしだと思う
- じゃあ「モーニング娘。」みたいのもNG?
- 日本と馴染みの薄い言語の文字はNGにしたい
- 簡体字・繁体字・ハングル・ロシア・アラビア
- RMT業者との戦いの歴史があるMMORPGでは特に中国語の印象よくない
- †聖天使猫姫†みたいな痛ネームは許容すべきか
簡体字と繁体字の除外
日本で使われてる漢字と、簡体字/繁体字は、文字コードのなかでゴチャマゼにないっているため、"一-龠"のような範囲指定では除外不可能。
一般的なゲームでも簡体字/繁体字を除外してるケースは少ないと思われる。
要件的にどうしても除外しなければいけないのなら、簡体字/繁体字テーブルのようなものを作って除外していくしかなさそう。
コード
public class NameChecker {
private static final String P_STR = "[^0-9a-zA-Zぁ-んァ-ヶ一-龠々ー]"
private static final Pattern P_NAME = Pattern.compile(P_STR);
// チェック処理
public static void check(String name) {
StringBuilder sb = new StringBuilder();
for (int i = 0, len = name.length(); i < len; i++) {
String s = name.substring(i, i + 1);
if (isMatch(s)) {
sb.append(s);
}
}
print(name, sb);
}
public static boolean isMatch(String s) {
Matcher matcher = P_NAME.matcher(s);
return matcher.find();
}
private static void print(String name, StringBuilder sb) {
if (sb.length() == 0) {
System.out.printf("OK: %s%n", name);
} else {
System.out.printf("NG: %d/%d %s [%s]%n", sb.length(), name.length(), name, sb.toString());
}
}
// テストコード
public static void main(String[] args) {
long start = System.currentTimeMillis();
// 半角数字
check("0123456789");
// 半角英小文字
check("abcdefghijklmnopqrstuvwxyz");
// 半角英大文字
check("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
// 全角ひらがな
check("あいうえお");
check("かきくけこ");
check("さしすせそ");
check("たちつてと");
check("なにぬねの");
check("はひふへほ");
check("まみむめも");
check("やゆよ");
check("らりるれろ");
check("わゐうゑをん");
check("がぎぐげご");
check("ざじずぜぞ");
check("だぢづでど");
check("ばびぶべぼ");
check("ぱぴぷぺぽ");
// 全角ひらがな小文字
check("ぁぃぅぇぉ");
check("ゃゅょ");
// 全角カタカナ
check("アイウエオ");
check("カキクケコ");
check("サシスセソ");
check("タチツテト");
check("ナニヌネノ");
check("ハヒフヘホ");
check("マミムメモ");
check("ヤユヨ");
check("ラリルレロ");
check("ワヰウヱヲンヴ");
check("カギグゲゴ");
check("ザジズゼゾ");
check("ダヂヅデド");
check("バビブベボ");
check("パピプペポ");
// 全角カタカナ小文字
check("ァィゥェォ");
check("ャュョ");
check("ヶ");
// 音引き
check("ー");
// 漢字
check("亜々赤白憂鬱幽霊");
check("嘉緒翠京桜靖ビス湖とっぽ束生夏陽夏照空々");
check("神生理美依羅炎皇斗幻の銀侍沙利菜愛利江留");
check("野風平蔵重親愛海月夢杏一将来織田信長");
check("邪王炎殺黒龍波");
// 簡体字
check("北京微梦创科网络技术有限公司");
check("对写处圆");
// 繁体字
check("亞假勛龍龜");
check("世棒經典賽王建民媽媽嘴大樂透劉真莫那魯道壽司");
System.out.println("---------- 以下NG ----------");
// 全角数字
check("0123456789");
// 全角英小文字
check("abcdefghijklmnopqrstuvwxyz");
// 全角英大文字
check("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
// 半角カタカナ
check("ァィゥェォ");
check("カキクケコ");
check("サシスセソ");
check("タチツテト");
check("ナニヌネノ");
check("ハヒフヘホ");
check("マミムメモ");
check("ヤユヨ");
// 中黒
check("・");
// 濁音、半濁音
check("゜゛");
// 空白
check(" ");
// 記号
check("!#$%&*+,-.:;=?@^_`|~");
check("!#$%&()*+,.―/ 0-9:;<=>?@A-Z[]^_`a-z{|} ̄");
check("()[]{}<>");
check("\"'");
check("\\");
check("○△□◇◎●▲■◆");
check("~~");
check("--");
check("♪♫♬");
check("※〒¶");
check("…‥。、∴∵,.");
check("↑→↓←⇐⇒⇔");
// 繰り返し記号
check("〃ゝゞヽヾ");
// 機種依存文字
check("①②③④⑤⑥⑦⑧⑨⑩");
// 特殊文字
check("†聖天使猫姫†");
// ハングル
check("아가다오쓰나는");
// キリル文字
check("АБВГҐДЃЂЕЄЀЁЖЗЅИІЇЙЍЈКЛЉМНЊОӨПРСТ");
check("абвгґдѓђеєѐёжзѕиіїйѝјклљмнњоөпрст");
// アラビア語
check("غة العربي");
System.out.println((System.currentTimeMillis() - start) + "ms");
}