ユーザー名を正規表現でチェックする

ゲームのユーザー名のチェック処理として適切な正規表現を考えてみる。
結論、これで事足りると思う。

```java
Pattern.compile("[^0-9a-zA-Zぁ-んァ-ヶ一-龠々ー]");
```

## 文字の種類

考慮すべき文字種の多すぎてまとめきれる気がしない。

- 全角ひらがな "あいうえお"
- 全角カタカナ "アイウエオ"
- 半角カタカナ "ｱｲｳｴｵ"
- 全角数字 "１２３４５"
- 半角数字 "12345"
- 全角英字大文字 "ＡＢＣＤＥ"
- 全角英字小文字 "ａｂｃｄｅ"
- 半角英字大文字 "ABCDE"
- 半角英字小文字 "abcde"
- 全角記号 "！＃＄％＆"
- 半角記号 "!#$%&"
- 漢字 "亜異兎絵尾"
- 長音 "ー"
- 中点 "・"
- 繰り返し記号 "〃ゝゞヽヾ"
- 機種依存文字 "①②③④⑤"
- キリル文字大文字 "АБВГҐ"
- キリル文字小文字 "абвгґ"
- ハングル "아가다오쓰"
- アラビア "غة العرب"

## 仕様の決め方

UI上の良し悪し（特に視認性）や、ゲームの世界観を判断材料にして決める。

- 顔文字はNGにしたい (・∀・)
  - 括弧をNGにすればよさそう
- ユーザー名に句点・読点はなしだと思う
  - じゃあ「モーニング娘。」みたいのもNG？
- 日本と馴染みの薄い言語の文字はNGにしたい
  - 簡体字・繁体字・ハングル・ロシア・アラビア
  - RMT業者との戦いの歴史があるMMORPGでは特に中国語の印象よくない
- †聖天使猫姫†みたいな痛ネームは許容すべきか

## 簡体字と繁体字の除外

日本で使われてる漢字と、簡体字／繁体字は、文字コードのなかでゴチャマゼにないっているため、"一-龠"のような範囲指定では除外不可能。
一般的なゲームでも簡体字／繁体字を除外してるケースは少ないと思われる。
要件的にどうしても除外しなければいけないのなら、簡体字／繁体字テーブルのようなものを作って除外していくしかなさそう。

## コード

```java
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("０１２３４５６７８９");
		// 全角英小文字
		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((System.currentTimeMillis() - start) + "ms");
	}
```