在我们的生产环境中,我们不时遇到Tomcat中一个非常奇怪的编码问题。
我还没有确切地指出问题发生在代码中的确切位置,但它涉及将非ascii字符替换为近似的ascii字符。
例如用'a'替换字符'å'。由于该网站是瑞典语,因此字符“å”,“ä”和“ö”非常常见。但由于某种原因,'ö'字符的替换总是有效,所以像“Köepinte grisenisäcken”这样的字符串变成了“Kop inte grisenisäcken”,即'ä'没有被取代,而'' ö'的角色是。
关于这个问题的一些快速事实:
它很少发生(我们已经注意到它3-4次,第一次可能是1 - 2年前)。
重启故障服务器会导致问题消失(直到下一次)。
它从未在同一时间出现在多个前端服务器上。
并不总是发生在同一台前端服务器上。
不涉及前端的用户输入。
所有前端服务器都连接到同一个CMS和DB,相关配置相同。
所有前端服务器都具有相同的相关配置(linux配置,tomcat配置,java环境配置,如“file.encoding”等),并使用相同的脚本启动(全部根据托管/服务提供商) )。
所有前端服务器都使用与网站相同的war文件和相同的jar文件。
发生此字符替换问题时,网站上没有其他编码问题。
我们无法在任何其他环境中重现此问题。
由于CMS要求,我们使用Tomcat 5.5和Java 5.
我只能想到这种行为的两个可能的原因:
托管服务提供商有时会以不同的方式启动/重新启动前端服务器,可能使用其他用户帐户和其他环境变量或其他文件访问权限,或者使用其他脚本而不是正常的脚本。
在Tomcat或webapp启动期间运行的某些进程取决于其他一些进程,有时(间歇性但很少)这两个(或更多)进程碰巧以导致此编码缺陷的顺序运行。
但即使上面有1或2,但它仍然没有完全解释究竟发生了什么。究竟有什么区别可以解释这个?由于所有“file.encoding”,“file.encoding.pkg”“sun.io.unicode.encoding”,“sun.jnu.encoding”和所有其他相关环境变量都在所有前端机器上匹配(使用视觉验证)调试页面,但问题出现了。)
有人会想到这种奇怪的间歇性行为的一些似是而非的解释吗?简单地升级Tomcat和/或Java版本并不是一个真正的相关答案,因为我们真的不知道这是否能解决问题,而且它仍然无法解释问题所在。我更感兴趣的是要确切地了解问题是由什么造成的。
此致 /吉米亨德里
的更新: 的
我想我找到了执行字符替换的代码。在启动时(由第一次执行替换调用触发),它构建一个HashMap< Character,String>,并像这样填充它:
lookup.put(new Character('å'), "a");
然后当它应该替换字符串的字符时,它会循环遍历每个字符,并且每个字符都在哈希映射中以charactar作为键进行查找,如果找到替换字符串则使用它,否则原始字符串使用字符被使用。
这部分代码已超过3年,由一位早已离开的开发人员编写。如果我今天要重写这段代码,我会做一些完全不同的事情,甚至可能解决问题。但它仍然无法准确解释究竟发生了什么。有人能看到一些可能的解释吗?
答案 0 :(得分:1)
在进行替换之前,将输入标准化为普通表格C.
例如,ä
可以只有1个字符U+00E4,也可以是两个字符,a
(U+0061)和组合分音{{3} }。
如果您的替代品只是查找撰写的表单,那么分解的表单仍将保留为\u0061\u0308
,因为这些表单都不匹配\u00e4
:
public static void main(String args[]) {
String decomposed = "\u0061\u0308";
String composed = "\u00e4";
System.out.println(decomposed);
System.out.println(composed);
System.out.println(composed.equals(decomposed));
System.out.println(Normalizer
.normalize(decomposed, Normalizer.Form.NFC).equals(composed));
}
输出
ä
ä
false
true