在生产环境中,我们有时会在Tomcat中遇到非常奇怪的编码问题。
我还不能精确指出问题在代码中的何处发生,但是它涉及将非ascii字符替换为近似的ascii字符。
例如,将字符“å”替换为“ a”。由于该网站是瑞典语,因此“å”,“ä”和“ö”字符非常常见。但是由于某种原因,始终可以替换'ö'字符,因此,像“Köpinte grisen isäcken”这样的字符串将变为“ Kop inte grisen isä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 lookup.put(new Character('å'), "a");
然后,当它应该替换字符串的字符时,它将循环遍历每个字符,并针对每个字符在字符映射表中以字符为键进行查找,如果找到替换字符串,则使用原始字符,否则将使用原始字符。
该代码的这一部分已有3年多的历史了,并且由一个已经走了很久的开发人员编写。如果我今天重写此代码,我会做一些完全不同的事情,这甚至可能解决问题。但是它仍然不能完全解释发生了什么。有人可以看到一些可能的解释吗?
最佳答案
在进行替换之前,将输入标准化为正常的C表格。
例如,ä
只能是1个字符,U+00E4,也可以是两个字符,即a
(U+0061)和组合音符U+0308。
如果您的替代人只是寻找组合形式,则分解后的形式仍将保留为\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
关于java - Tomcat服务器中奇怪的间歇字符编码行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13989559/