Java で Unicode の Fullwidth や Halfwidth を判定する方法 

ICU4J ライブラリーを使って Unicode の East Asian Width プロパティーを取得することで、Unicode としての全角・半角を判定することができます。

```java
import com.ibm.icu.lang.CharacterProperties;
import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.lang.UProperty;
import com.ibm.icu.util.CodePointMap;

public class EastAsianWidthChecker {
    private static final CodePointMap eastAsianWidthMap =
            CharacterProperties.getIntPropertyMap(UProperty.EAST_ASIAN_WIDTH);

    /**
     * @return {@link UCharacter.EastAsianWidth#NARROW NARROW},
     *         {@link UCharacter.EastAsianWidth#HALFWIDTH HALFWIDTH},
     *         {@link UCharacter.EastAsianWidth#AMBIGUOUS AMBIGUOUS},
     *         {@link UCharacter.EastAsianWidth#FULLWIDTH FULLWIDTH},
     *         {@link UCharacter.EastAsianWidth#WIDE WIDE},
     *         {@link UCharacter.EastAsianWidth#NEUTRAL NEUTRAL} のいずれか。
     */
    public static int eastAsianWidthOf(int codePoint) {
        return eastAsianWidthMap.get(codePoint);
    }
}
```

戻り値の NARROW と HALFWIDTH は半角、WIDE と FULLWIDTH は全角、NEUTRAL はどちらでもない文字を表します。

AMBIGUOUS はギリシャ文字のように東アジアで2バイト文字だけれどもヨーロッパでは1バイト文字であるものに使われます。Unicode としては曖昧な幅ですが、我々にとっては全角であることが多いでしょう。以下では全角として扱っていますが、場合によりけりだと思います。


## 動かしてみた

次のようなクラスを作って実行してみました。switch 式を使ってしまったので Java 14 以降が必要です。

```java
import static com.ibm.icu.lang.UCharacter.EastAsianWidth.*;

public class Test {
    public static void main(String[] args) {
        "aａβ○ｶがが茶茶丳𠮷㍍㌀😄क".codePoints().forEach(codePoint -> {
            int unicodeWidth = EastAsianWidthChecker.eastAsianWidthOf(codePoint);
            String result = switch (unicodeWidth) {
                case NARROW -> "半角 (Narrow)";
                case HALFWIDTH -> "半角 (Halfwidth)";
                case AMBIGUOUS -> "全角 (Ambiguous)";
                case FULLWIDTH -> "全角 (Fullwidth)";
                case WIDE -> "全角 (Wide)";
                case NEUTRAL -> "どちらでもない (Neutral)";
                default -> "どちらでもない (" + unicodeWidth + ")";
            };

            System.out.printf(
                    "「%s」(U+%04X) は Unicode で%s。%n",
                    Character.toString(codePoint), codePoint, result
            );
        });
    }
}
```

実行結果は次のようになりました。

```
「a」(U+0061) は Unicode で半角 (Narrow)。
「ａ」(U+FF41) は Unicode で全角 (Fullwidth)。
「β」(U+03B2) は Unicode で全角 (Ambiguous)。
「○」(U+25CB) は Unicode で全角 (Ambiguous)。
「ｶ」(U+FF76) は Unicode で半角 (Halfwidth)。
「が」(U+304C) は Unicode で全角 (Wide)。
「か」(U+304B) は Unicode で全角 (Wide)。
「゙」(U+3099) は Unicode で全角 (Wide)。
「茶」(U+8336) は Unicode で全角 (Wide)。
「茶」(U+F9FE) は Unicode で全角 (Wide)。
「丳」(U+4E33) は Unicode で全角 (Wide)。
「𠮷」(U+20BB7) は Unicode で全角 (Wide)。
「㍍」(U+334D) は Unicode で全角 (Wide)。
「㌀」(U+3300) は Unicode で全角 (Wide)。
「😄」(U+1F604) は Unicode で全角 (Wide)。
「क」(U+0915) は Unicode でどちらでもない (Neutral)。
「」(U+E000) は Unicode で全角 (Ambiguous)。
```

U+E000 は私用領域なので、本当に全角かは場合によると思います。Qiita では文字化けしていますが、手元の環境では全角の虫眼鏡が表示されます。


## 注意点

結合文字には対応していません。例えば「か」と「濁点」を表す "\u304b\u3099" は意味的には「が」を表しますが、2つのコード ポイントですので、上記の方法では1文字とは認識できません。同様に、書記素クラスター (grapheme cluster) もコード ポイントが複数になることがありますので、対応していません。


## 参考

https://en.wikipedia.org/wiki/Halfwidth_and_fullwidth_forms

https://unicode-org.github.io/icu-docs/apidoc/released/icu4j/