Hangi kütüphane kullanılacak?
Bu yazı itibariyle, ortaya çıkan üç kütüphane:
Apache Any23'ü dahil etmiyorum çünkü kaputun altında ICU4j 3.4 kullanıyor.
Hangisinin doğru karakter kümesini algıladığını (veya mümkün olduğunca yakın) nasıl söyleyebilirim ?
Yukarıdaki kütüphanelerin tespit ettiği karakter kümesini onaylamak imkansızdır. Bununla birlikte, onlara sırayla sormak ve iade edilen cevabı puanlamak mümkündür.
Geri verilen cevap nasıl puanlanır?
Her yanıta bir nokta atanabilir. Bir yanıt ne kadar fazla puana sahip olursa, algılanan karakter kümesine o kadar güven duyulur. Bu basit bir puanlama yöntemidir. Diğerlerini detaylandırabilirsiniz.
Örnek kod var mı?
Önceki satırlarda açıklanan stratejiyi uygulayan tam bir pasaj.
public static String guessEncoding(InputStream input) throws IOException {
// Load input data
long count = 0;
int n = 0, EOF = -1;
byte[] buffer = new byte[4096];
ByteArrayOutputStream output = new ByteArrayOutputStream();
while ((EOF != (n = input.read(buffer))) && (count <= Integer.MAX_VALUE)) {
output.write(buffer, 0, n);
count += n;
}
if (count > Integer.MAX_VALUE) {
throw new RuntimeException("Inputstream too large.");
}
byte[] data = output.toByteArray();
// Detect encoding
Map<String, int[]> encodingsScores = new HashMap<>();
// * GuessEncoding
updateEncodingsScores(encodingsScores, new CharsetToolkit(data).guessEncoding().displayName());
// * ICU4j
CharsetDetector charsetDetector = new CharsetDetector();
charsetDetector.setText(data);
charsetDetector.enableInputFilter(true);
CharsetMatch cm = charsetDetector.detect();
if (cm != null) {
updateEncodingsScores(encodingsScores, cm.getName());
}
// * juniversalchardset
UniversalDetector universalDetector = new UniversalDetector(null);
universalDetector.handleData(data, 0, data.length);
universalDetector.dataEnd();
String encodingName = universalDetector.getDetectedCharset();
if (encodingName != null) {
updateEncodingsScores(encodingsScores, encodingName);
}
// Find winning encoding
Map.Entry<String, int[]> maxEntry = null;
for (Map.Entry<String, int[]> e : encodingsScores.entrySet()) {
if (maxEntry == null || (e.getValue()[0] > maxEntry.getValue()[0])) {
maxEntry = e;
}
}
String winningEncoding = maxEntry.getKey();
//dumpEncodingsScores(encodingsScores);
return winningEncoding;
}
private static void updateEncodingsScores(Map<String, int[]> encodingsScores, String encoding) {
String encodingName = encoding.toLowerCase();
int[] encodingScore = encodingsScores.get(encodingName);
if (encodingScore == null) {
encodingsScores.put(encodingName, new int[] { 1 });
} else {
encodingScore[0]++;
}
}
private static void dumpEncodingsScores(Map<String, int[]> encodingsScores) {
System.out.println(toString(encodingsScores));
}
private static String toString(Map<String, int[]> encodingsScores) {
String GLUE = ", ";
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, int[]> e : encodingsScores.entrySet()) {
sb.append(e.getKey() + ":" + e.getValue()[0] + GLUE);
}
int len = sb.length();
sb.delete(len - GLUE.length(), len);
return "{ " + sb.toString() + " }";
}
Gelişmeler:guessEncoding
yöntem InputStream tamamen okur. Büyük girdi akışları için bu bir endişe kaynağı olabilir. Bütün bu kütüphaneler girdi akışının tamamını okuyacaktır. Bu, karakter setini tespit etmek için büyük bir zaman tüketimi anlamına gelir.
İlk veri yüklemesini birkaç bayt ile sınırlamak ve karakter kümesi algılamasını yalnızca bu bayt üzerinde yapmak mümkündür.