다음 스레드를 참조하십시오.
Java App : ISO-8859-1 인코딩 파일을 올바르게 읽을 수 없습니다
입력 스트림 / 파일의 올바른 문자 세트 인코딩을 프로그래밍 방식으로 결정하는 가장 좋은 방법은 무엇입니까?
나는 다음을 사용하려고 시도했다.
File in = new File(args[0]);
InputStreamReader r = new InputStreamReader(new FileInputStream(in));
System.out.println(r.getEncoding());
그러나 ISO8859_1로 인코딩 된 파일에서 위의 코드는 ASCII를 생성하며 올바르지 않습니다. 파일의 내용을 콘솔에 올바르게 렌더링 할 수 없습니다.
답변
Java에서 인코딩을 감지하기 위해 jchardet과 유사한이 라이브러리를 사용했습니다 :
http://code.google.com/p/juniversalchardet/
답변
임의 바이트 스트림의 인코딩을 결정할 수 없습니다. 이것이 인코딩의 특성입니다. 인코딩은 바이트 값과 해당 표현 간의 매핑을 의미합니다. 따라서 모든 인코딩은 “올바른”것이 될 수 있습니다.
GetEncoding이 () 메소드 (판독 세워졌다 부호화 반환 javadoc는 스트림을 위해). 인코딩을 추측하지 않습니다.
일부 스트림은이를 생성하는 데 사용 된 인코딩 (XML, HTML)을 알려줍니다. 그러나 임의의 바이트 스트림은 아닙니다.
어쨌든 필요한 경우 직접 인코딩을 추측 할 수 있습니다. 모든 언어는 모든 문자마다 공통된 빈도를 갖습니다. 영어에서는 문자 e가 매우 자주 나타나지만 ê는 거의 나타나지 않습니다. ISO-8859-1 스트림에는 일반적으로 0x00 문자가 없습니다. 그러나 UTF-16 스트림에는 많은 것이 있습니다.
또는 : 사용자에게 요청할 수 있습니다. 이미 다른 인코딩으로 파일 스 니펫을 제공하는 애플리케이션을 보았으며 “올바른”것을 선택하도록 요청했습니다.
답변
이것을 확인하십시오 :
http://site.icu-project.org/ (icu4j) IOStream에서 문자 세트를 감지하는 라이브러리가 다음과 같이 간단 할 수 있습니다.
BufferedInputStream bis = new BufferedInputStream(input);
CharsetDetector cd = new CharsetDetector();
cd.setText(bis);
CharsetMatch cm = cd.detect();
if (cm != null) {
reader = cm.getReader();
charset = cm.getName();
}else {
throw new UnsupportedCharsetException()
}
답변
내가 가장 좋아하는 것은 다음과 같습니다.
의존:
<dependency>
<groupId>org.apache.any23</groupId>
<artifactId>apache-any23-encoding</artifactId>
<version>1.1</version>
</dependency>
견본:
public static Charset guessCharset(InputStream is) throws IOException {
return Charset.forName(new TikaEncodingDetector().guessEncoding(is));
}
의존:
<dependency>
<groupId>org.codehaus.guessencoding</groupId>
<artifactId>guessencoding</artifactId>
<version>1.4</version>
<type>jar</type>
</dependency>
견본:
public static Charset guessCharset2(File file) throws IOException {
return CharsetToolkit.guessEncoding(file, 4096, StandardCharsets.UTF_8);
}
답변
파일을 디코딩 하고 “잘못된 입력”또는 “매핑 불가능한 문자”오류를 관찰 하여 특정 문자 세트에 대해 파일의 유효성 을 확실히 검증 할 수 있습니다 . 물론, 이것은 문자셋이 잘못되었는지 알려줍니다. 그것이 정확한지 알려주지 않습니다. 이를 위해서는 디코딩 된 결과를 평가하기위한 비교 기준이 필요합니다. 예를 들어 문자가 일부 하위 세트로 제한되는지 또는 텍스트가 엄격한 형식을 준수하는지 여부를 미리 알고 있습니까? 결론은 문자셋 탐지가 보장없이 추측 할 수 있다는 것입니다.CharsetDecoder
답변
어떤 라이브러리를 사용해야합니까?
이 글을 쓰는 시점에서 다음과 같은 세 가지 라이브러리가 있습니다.
Apache Any23 은 ICU4j 3.4를 사용하기 때문에 포함하지 않습니다 .
어느 것이 올바른 문자 세트를 감지했는지 (또는 가능한 한 가깝게) 말하는지?
위의 각 라이브러리에서 감지 한 문자 세트를 인증하는 것은 불가능합니다. 그러나 차례로 요청하고 반환 된 응답의 점수를 매길 수 있습니다.
반환 된 응답의 점수를 매기는 방법?
각 응답에는 한 지점이 할당 될 수 있습니다. 응답이 많을수록 탐지 된 문자 집합의 신뢰도가 높아집니다. 이것은 간단한 채점 방법입니다. 다른 사람들을 정교하게 만들 수 있습니다.
샘플 코드가 있습니까?
다음은 이전 행에서 설명한 전략을 구현하는 전체 스 니펫입니다.
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() + " }";
}
개선 :
이 guessEncoding
방법은 입력 스트림을 완전히 읽습니다. 큰 입력 스트림의 경우 이것이 문제가 될 수 있습니다. 이 모든 라이브러리는 전체 입력 스트림을 읽습니다. 이는 문자셋을 탐지하는 데 많은 시간이 소요됨을 의미합니다.
초기 데이터로드를 몇 바이트로 제한하고 그 몇 바이트에서만 문자 세트 감지를 수행 할 수 있습니다.
답변
위의 libs는 파일의 시작 부분에 BOM이있는 경우에만 작동하는 간단한 BOM 검출기입니다. 텍스트를 스캔하는 http://jchardet.sourceforge.net/ 을 살펴보십시오.