1

ときどき、本番環境の Tomcat で非常に奇妙なエンコーディングの問題に遭遇しました。

コードのどこで問題が発生したかを正確に特定することはまだできていませんが、ASCII 以外の文字を近似の ASCII 文字に置き換える必要があります。

たとえば、文字「å」を「a」に置き換えます。このサイトはスウェーデン語であるため、'å'、'ä'、'ö' の文字は非常に一般的です。しかし、何らかの理由で 'ö' 文字の置換は常に機能するため、"Köp inte grisen i säcken" のような文字列は "Kop inte grisen i säcken" になります。 ö'文字です。

問題に関するいくつかの簡単な事実:

  • めったに発生しません (最初はおそらく 1 ~ 2 年前に 3 ~ 4 回気づいています)。

  • 問題のあるサーバーを再起動すると、問題が解消されます (次回まで)。

  • 同時に複数のフロント エンド サーバーで発生したことはありません。

  • 同じフロントエンド サーバーで常に発生するとは限りません。

  • フロントエンドでのユーザー入力は必要ありません。

  • すべてのフロント エンド サーバーは同じ CMS と DB に接続し、関連する構成は同一です。

  • すべてのフロント エンド サーバーは同じ関連構成 (Linux 構成、Tomcat 構成、"file.encoding" などの Java 環境構成) を持ち、同じスクリプトを使用して開始されます (すべてホスティング/サービス プロバイダーによる)。

  • すべてのフロント エンド サーバーは、サイトに対してまったく同じ war ファイルと同じ jar ファイルを使用します。

  • この文字置換の問題が発生している間、サイトで他のエンコーディングの問題は見られません。

  • 他の環境で問題を再現できたことはありません。

CMS 要件のため、Tomcat 5.5 と Java 5 を使用しています。

この動作のもっともらしい原因は 2 つしか考えられません。

  1. ホスティング プロバイダーは、別の方法でフロント エンド サーバーを起動/再起動することがあります。おそらく、別の環境変数または別のファイル アクセス権を持つ別のユーザー アカウントを使用するか、通常のスクリプトとは別のスクリプトを使用します。

  2. Tomcat または webapp の起動中に実行されているプロセスは、他のプロセスに依存しており、これら 2 つ (またはそれ以上) のプロセスが、このエンコーディングの欠陥を引き起こす順序で実行されることがあります (断続的ではありますが、めったにありません)。

しかし、上記の 1 または 2 が当てはまるとしても、実際に何が起こるかを完全には説明していません。これを説明できる正確な違いは何ですか?「file.encoding」、「file.encoding.pkg」、「sun.io.unicode.encoding」、「sun.jnu.encoding」、およびその他すべての関連する環境変数はすべて、すべてのフロント エンド マシンで一致するため (問題が発生している間、デバッグ ページ)。

この奇妙な断続的な動作について、もっともらしい説明を思いつく人はいますか? 単純に Tomcat や Java のバージョンをアップグレードすることは、問題が解決するかどうかわからないため、実際には適切な回答ではありません。問題の原因を正確に理解することにもっと興味があります。

よろしく/ジミ

アップデート:

文字置換を実行するコードを見つけたと思います。開始時に (置換を行うための最初の呼び出しによってトリガーされます)、HashMap<Character, String> を構築し、次のように入力します。

lookup.put(new Character('å'), "a");  

次に、文字列の文字を置き換える必要がある場合は、各文字をループし、各文字について、キーとして文字を使用してハッシュ マップでルックアップを行い、置換文字列が見つかった場合はそれが使用され、そうでない場合は元の文字が使用されます。 .

コードのこの部分は 3 年以上前のものであり、ずっと前に亡くなった開発者によって書かれています。今日このコードを書き直すとしたら、まったく別のことをすることになり、それによって問題が解決することさえあります。しかし、何が起こったのかを正確に説明することはまだできません。誰かが可能な説明を見ることができますか?

4

1 に答える 1

1

置換を行う前に、入力を通常の Form C に正規化します。

たとえば、 U+00E4äという 1 文字だけにすることも、2 文字( U+0061 ) と分音記号U+0308を組み合わせたものにすることもできます。a

置換が合成されたフォームを探すだけの場合、分解されたフォームはそのまま残ります\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
于 2012-12-21T12:58:35.500 に答える