之前一段时间排查一个应用在中文目录下无法启动的问题,查过一个 Equinox 的 Manifest 处理的坑。今天,一个同事在写 Equinox 插件的时候也遇到了类似的问题。这里记录一下 Equinox 里面对 Manifest 中的中文处理的坑。
问题描述
先来看一段代码:
package manifest;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.Manifest;
import org.eclipse.osgi.util.ManifestElement;
import org.osgi.framework.BundleException;
public class Test {
private static String chineseString = "你好,世界你好,世界你好,世界你好,世界你好,世界你好,世界你好,世界你好,世界你好,世界你好,世界你好,世界你好,世界";
public static void main(String[] args) throws IOException, BundleException {
File manifestFile = createManifest();
// 使用 ManifestElement 读取
Map<String, String> header = new HashMap<>();
ManifestElement.parseBundleManifest(new FileInputStream(manifestFile), header);
System.out.println(header.get("Test-Headers"));
}
private static File createManifest() throws IOException {
Manifest manifest = new Manifest();
manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
// 创建一个带有中文的 Header,MANIFEST.MF 的一行的最大长度是 72 字节,中文在这里会被阶段
manifest.getMainAttributes().putValue("Test-Headers", chineseString);
File file = new File("MANIFEST.MF");
if (!file.exists()) {
file.createNewFile();
}
OutputStream outputStream = new FileOutputStream(file);
manifest.write(outputStream);
outputStream.flush();
outputStream.close();
return file;
}
}
这一段代码虽然比较长,但是实际上的内容其实不多,主要做了这几步:
- 创建一个内容带有中文的 MANIFEST.MF 文件。
- 写入 MANIFEST.MF 文件到磁盘上。
- 用 Equinox 的
ManifestElement
来读取 MANFIEST.MF。 - 输出刚才的带有中文的 Header
上面的这段代码在我本地的执行结果如下:
你好,世界你好,世界你好,世界你好,��界你好,世界你好,世界你好,世界你好,世界你��,世界你好,世界你好,世界你好,世界
大家可以看到,中间有几个地方出现了乱码,同样的代码,如果用标准的 JDK 的方式来读取,是不会出现这种情况,为什么呢?
原因
首先,我们知道 UTF-8 的中文是占据了三个字节,而 MANIFEST.MF 文件一行的最大长度是 72 个字节,也就是说,如果你的 MANIFEST.MF 中含有中文,那么这个中文的三个字节可能会被截断,出现一部分在上面一行,一部分在下面一行的情况。上面的 MANIFEST.MF 文件就出现了这种情况,可以 cat 这个文件来看一下:
[~/Desktop/test]$ cat MANIFEST.MF
Manifest-Version: 1.0
Test-Headers: 你好,世界你好,世界你好,世界你好,�
�界你好,世界你好,世界你好,世界你好,世界你�
�,世界你好,世界你好,世界你好,世界
不过,即使这样写入了,如果读取的时候完全按照字节来读取的话,那也应该没有问题。但是,Equinox 比较特立独行,看下 Equinox 读取 Manifest 的关键代码:
作死的 Equinox 将一行读取出来以后直接转成了一个 string,而 byte 在转 string 的时候,如果遇到无法转的字节的话,会用 �
来替代,于是就出现了上面的情况。(关于 �
,可以看 http://en.wikipedia.org/wiki/Specials_(Unicode_block))
解决方法
这个问题除非 Equinox 修复了此 Bug,否则是无解的。只能说在用到 Equinox 的时候,尽量不要使用中文的 Header 吧。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。