爬蟲、郵件解析、異構系統集成時,目標頁面往往未聲明或錯誤聲明編碼。強行按 UTF-8 解碼會導致亂碼,必須實現自動檢測。
Content-Type優先級最高,HTTP 協議標準字段。
Content-Type: text/html; charset=GBK
public String detectFromHttpHeader(HttpURLConnection conn) {
String contentType = conn.getContentType(); // 如 "text/html; charset=GB2312"
if (contentType != null) {
Matcher m = Pattern.compile("charset\\s*=\\s*([^\\s;]+)").matcher(contentType);
if (m.find()) return m.group(1).trim();
}
return null;
}
<meta> 標籤HTML4/5 兩種寫法:
<!-- HTML4 --> <meta http-equiv="Content-Type" content="text/html; charset=GB2312"> <!-- HTML5 --> <meta charset="UTF-8">
public String detectFromHtmlMeta(String html) {
// HTML5 風格
Matcher m5 = Pattern.compile("<meta\\s+charset\\s*=\\s*[\"']?([^\"'>]+)").matcher(html);
if (m5.find()) return m5.group(1).trim();
// HTML4 風格
Matcher m4 = Pattern.compile("<meta\\s+http-equiv\\s*=\\s*[\"']Content-Type[\"']\\s+content\\s*=\\s*[\"'][^\"']*charset\\s*=\\s*([^\"'>]+)").matcher(html);
if (m4.find()) return m4.group(1).trim();
return null;
}
基於字節頻率統計推斷編碼,適用於無聲明頁面。
<dependency>
<groupId>com.chenlb.mmseg4j</groupId>
<artifactId>cpdetector</artifactId>
<version>1.0.12</version>
</dependency>
import cpdetector.io.*;
public class CharsetDetector {
private static final CodepageDetectorProxy detector = CodepageDetectorProxy.getInstance();
static {
// 添加解析器(優先級遞減)
detector.add(new ParsingDetector(false)); // 1. 解析 HTML meta
detector.add(JChardetFacade.getInstance()); // 2. Mozilla 統計算法
detector.add(ASCIIDetector.getInstance()); // 3. ASCII 兜底
detector.add(UnicodeDetector.getInstance()); // 4. Unicode BOM
}
public static String detect(byte[] content) {
try {
java.nio.charset.Charset charset = detector.detectCodepage(new ByteArrayInputStream(content));
return charset != null ? charset.name() : "UTF-8";
} catch (Exception e) {
return "UTF-8"; // 最終兜底
}
}
public static String detect(URL url) throws IOException {
return detect(IOUtils.toByteArray(url.openStream()));
}
}
public String detectCharset(URL url, byte[] content) {
// 1. HTTP 頭
String charset = detectFromHttpHeader(url.openConnection());
if (charset != null) return normalizeCharset(charset);
// 2. HTML Meta
String html = new String(content, StandardCharsets.ISO_8859_1); // 先用不丟字節的編碼讀出
charset = detectFromHtmlMeta(html);
if (charset != null) return normalizeCharset(charset);
// 3. Cpdetector 統計檢測
return CharsetDetector.detect(content);
}
private String normalizeCharset(String charset) {
return Charset.forName(charset).name(); // 標準化大小寫、別名
}
| 常見別名 | 標準名稱 |
|---|---|
| GB2312 / GBK / GB18030 | GBK (Java 以 GBK 覆蓋 GB2312/GB18030) |
| UTF8 / utf-8 | UTF-8 |
| ISO-8859-1 / Latin1 | ISO-8859-1 |
ISO_8859_1 是「不丟字節」編碼,適合作為中轉讀取 HTML 源碼。BOM 頭(UTF-8/16/32),UnicodeDetector 會自動識別。生產環境推薦三級兜底鏈:HTTP Head → HTML Meta → Cpdetector 統計檢測。代碼已封裝為可復用工具類,直接集成項目即可。