zip格式文件编码检测
解压后文件名乱码
由于zip格式文件无编码存储的结构,因此解压时无法知道原先的编码。
当解压zip格式文件时使用的编码和原编码不一致时,就可能会出现解压后文件名乱码问题。
猜测编码
基于上述问题,需要对zip格式文件所使用的编码进行猜测。
笔者使用基于统计学原理的 juniversalchardet
库来猜测编码,下面是maven依赖坐标。
<dependency>
<groupId>com.googlecode.juniversalchardet</groupId>
<artifactId>juniversalchardet</artifactId>
<version>1.0.3</version>
</dependency>
下面是一个简单的例子,只需要提供待猜测文本的字节数据即可。
UniversalDetector detector = new UniversalDetector(null);
String text = "这是一段测试文本";
byte[] buf = text.getBytes("GB18030");
detector.handleData(buf, 0, buf.length);
detector.dataEnd();
System.out.println(detector.getDetectedCharset()); // GB18030
zip格式文件结构
zip格式文件的结构如下(具体解析见其他文章),这里需要其中的 CENTRAL_DIRCETORY_HEADER
区域,该区域存储zip里所有的文件及文件夹的名称。
|LOCAL_FILE_HEADER|
|EXTRA_LOCAL_HEADER|
|CENTRAL_DIRCETORY_HEADER|
|END_OF_CENTRAL_DIRCETORY_RECORD|
利用Apache Compress库获取名称字节数据
笔者一般推荐使用 Apache Compress
库来处理zip格式文件,可以使用 ZipArchiveEntry.getRawName
方法来获取名称的字节数据。
下面是检测zip格式文件编码的例子,其中为了加快速度忽略解析Local file header区域。
public String detectCharset(File file) throws IOException {
// Ignore resolve local file header to improve rate.
ZipFile zipFile = new ZipFile(file, StandardCharsets.UTF_8.name(), true, false);
Enumeration<ZipArchiveEntry> entries = zipFile.getEntries();
UniversalDetector detector = new UniversalDetector(null);
while (entries.hasMoreElements()) {
ZipArchiveEntry entry = entries.nextElement();
byte[] buf = entry.getRawName(); // Get the byte data of file name.
detector.handleData(buf, 0, buf.length);
}
detector.dataEnd();
return detector.getDetectedCharset();
}
下面是Apache Compress库的依赖坐标。
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.21</version>
</dependency>
注意事项
数据量越大 juniversalchardet
库检测编码就越准确,反之可能得到空结果或者不准确的编码。
例如猜测编码章节中让 text="中文"
,最后输出的结果是 KOI8-R
。
为此实际使用中要根据业务场景来做限定,比方说系统面向的用户只可能是中文用户,那么当出现 KOI8-R
这种斯拉夫文字的编码时应当限定为中文的 GB18030
编码(数据量小时GB18030非常大概率会被检测成KOI8-R)。