ASCIIはUTF-8のサブセット(一部分であるという意味)です。そして、ASCIIはShift_JISのサブセットでもあります。集合で言うと、
ASCII ⊂ UTF-8
ASCII ⊂ Shfit_JIS
のような関係です。ASCIIの文字はUTF-8としてもShift_JISとしてもどちらで解釈しても正しいです。ですので、ASCIIの文字だけで構成されているバイト列が本来どのエンコードとして書かれたものなのかを区別する方法はありません。
それはさておき、書かれているコードには問題があります。Stringにしている時点でJavaではUnicode(内部的にはUTF-16)で正しく処理されています。UTF-8はUnicodeに対する完全なマッピングを持っているため、StringをUTF-8のバイト列にする処理が失敗することはありません。また、StringをUTF-8のバイト列に変換後、UTF-8として解釈したときに、元のStringと同じにならないこともありません。比較すべきはStringにする前のbyte配列です。
次に、文字コード指定に"UTF-8"という文字列を使わずにjava.nio.charset.StandardCharsets.UTF_8
を使用すべきです。文字コードを表す文字列から該当するエンコードをサポートしているかどうかの処理が省かれる(そして、例外処理も不要)という利点と、"UDF-8"とかスペルミスしていた場合にコンパイル時にわかるという利点があります。
最後に、長い文字列を処理しようとした場合、文字列の生成や文字列全体の比較という重い処理が入ります。わざわざ比較しなくてもデコード自体に失敗したらUTF-8ではないと判断した方がいいです。
これらを踏まえて、3種類ほどサンプルとなる関数を作ってみました。
Java
1import java.util.Arrays;
2import java.nio.ByteBuffer;
3import java.nio.charset.Charset;
4import java.nio.charset.StandardCharsets;
5import java.nio.charset.CharacterCodingException;
6
7public class EncodeChecker {
8 public static boolean check1(byte[] bytes, Charset cs) {
9 return Arrays.equals(new String(bytes, cs).getBytes(cs), bytes);
10 }
11 public static boolean check1(byte[] bytes) {
12 return check1(bytes, StandardCharsets.UTF_8);
13 }
14
15 public static boolean check2(byte[] bytes, Charset cs) {
16 return cs.encode(cs.decode(ByteBuffer.wrap(bytes)))
17 .equals(ByteBuffer.wrap(bytes));
18 }
19 public static boolean check2(byte[] bytes) {
20 return check2(bytes, StandardCharsets.UTF_8);
21 }
22
23 public static boolean check3(byte[] bytes, Charset cs) {
24 try {
25 cs.newDecoder().decode(ByteBuffer.wrap(bytes));
26 return true;
27 } catch (CharacterCodingException e) {
28 return false;
29 }
30 }
31 public static boolean check3(byte[] bytes) {
32 return check3(bytes, StandardCharsets.UTF_8);
33 }
34}
check1
は文字列を作ってbyet配列にして比較です。あまり変わりません。
check2
は文字列を作らずにByteBufferだけで処理しています。String生成が無い分、check1より速い気がします。
check3
は単純にdecodeに失敗したらfalseを返すようにしています。比較が無い分、たぶん一番速いです。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。