Welche Bibliothek soll ich benutzen?
Zum jetzigen Zeitpunkt entstehen drei Bibliotheken:
Ich schließe Apache Any23 nicht ein da es ICU4j 3.4 unter der Haube verwendet.
Wie man erkennt, wer das Recht erkannt hat Zeichensatz (oder so nah wie möglich)?
Es ist unmöglich, den von den oben genannten Bibliotheken erkannten Zeichensatz zu zertifizieren. Es ist jedoch möglich, sie nacheinander zu fragen und die zurückgegebene Antwort zu bewerten.
Wie kann ich die zurückgegebene Antwort bewerten?
Jeder Antwort kann ein Punkt zugewiesen werden. Je mehr Punkte eine Antwort hat, desto mehr Vertrauen hat der erkannte Zeichensatz. Dies ist eine einfache Bewertungsmethode. Sie können andere ausarbeiten.
Gibt es einen Beispielcode?
Hier ist ein vollständiger Ausschnitt, der die in den vorherigen Zeilen beschriebene Strategie implementiert.
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() + " }";
}
Verbesserungen:
DieguessEncoding
Methode liest den Eingabestream vollständig. Bei großen Eingangsströmen kann dies ein Problem sein. Alle diese Bibliotheken würden den gesamten Eingabestream lesen. Dies würde einen großen Zeitaufwand für die Erkennung des Zeichensatzes bedeuten.
Es ist möglich, das anfängliche Laden von Daten auf einige Bytes zu beschränken und die Zeichensatzerkennung nur für diese wenigen Bytes durchzuführen.