世界のどの地域にも独自の現地言語があります。地域の違いは言語環境の違いに直結します。国際的なプログラムを開発する過程では、言語の問題に対処することが重要です。
これは世界中に存在する問題であるため、Java は世界的な解決策を提供します。この記事で説明する方法は中国語を処理するためのものですが、拡張すると、世界の他の国や地域の言語の処理にも適用できます。
漢字は全角です。いわゆるダブル バイトとは、ダブル ワードが 2 つのバイト位置 (つまり 16 ビット) を占めることを意味し、それぞれ上位ビットと下位ビットと呼ばれます。中国で指定されている中国語の文字エンコーディングは GB2312 であり、現在、中国語を処理できるほとんどすべてのアプリケーションが GB2312 をサポートしています。 GB2312 には、第 1 水準と第 2 水準の漢字と 9 つの領域記号が含まれており、上位ビットは 0xa1 から 0xfe まで、下位ビットも 0xa1 から 0xfe までの範囲になります。このうち、漢字のエンコーディング範囲は 0xb0a1 から 0xf7fe です。
GBK と呼ばれる別のエンコーディングもありますが、これは仕様であり、必須ではありません。 GBK は GB2312 と互換性のある 20902 文字の中国語を提供し、エンコード範囲は 0x8140 ~ 0xfefe です。 GBK のすべての文字を 1 つずつ Unicode 2.0 にマッピングできます。
近い将来、中国は別の規格 GB18030-2000 (GBK2K) を公布する予定です。チベット、モンゴル、その他の少数民族のフォントが含まれており、文字の位置が不十分であるという問題を根本的に解決します。注: 固定長ではなくなりました。 2バイト部分はGBKと互換性があり、4バイト部分は拡張文字とグリフです。最初と 3 バイトの範囲は 0x81 ~ 0xfe、2 バイトと 4 バイトの範囲は 0x30 ~ 0x39 です。
この記事は Unicode を紹介するものではありません。興味のある方は、「http://www.unicode.org/」を参照して詳細を参照してください。 Unicode には、世界中のすべての文字グリフが含まれているという特徴があります。したがって、さまざまな地域の言語は Unicode とのマッピング関係を確立でき、Java はこれを利用して異種言語間の変換を実現します。
JDK では、中国語に関連するエンコーディングは次のとおりです。
表 1 JDK の中国語に関連するエンコーディングのリスト
エンコーディング名の | 説明 |
ASCII | 7 ビット、ascii7 と同じ |
ISO8859-1 | 8 ビット、8859_1、ISO-8859-1、ISO_8859-1、latin1 などと同じ |
GB2312-80 | 16 ビット、gb2312、gb2312 と同じ-1980、EUC_CN、euccn、1381、Cp1381、1383、Cp1383、ISO2022CN、ISO2022CN_GB などは |
MS936 | と同じです。 注: 大文字と小文字を区別する |
は | GB18030 |
と | 同じです。 |
cp1392 および 1392 と同じです |
。現在、実際にサポートされている JDK はほとんどありませんが、プログラミングの際によく使用するのは GB2312 (GBK) と ISO8859-1 です。
なぜ「?」記号があるのでしょうか?
前述したように、異なる言語間の変換は Unicode を通じて行われます。 2 つの異なる言語 A と B があるとします。変換手順は次のとおりです。まず A を Unicode に変換し、次に Unicode を B に変換します。
例を挙げてください。 GB2312 には漢字「李」があり、そのコードは「C0EE」であり、ISO8859-1 コードに変換する必要があります。手順は次のとおりです。まず文字「李」を Unicode に変換して「674E」を取得し、次に「674E」を ISO8859-1 文字に変換します。もちろん、ISO8859-1 には「674E」に対応する文字がないため、このマッピングは成功しません。
問題はマッピングが失敗した場合に発生します。特定の言語から Unicode に変換する場合、その文字が特定の言語に存在しない場合、結果は Unicode コード「uffffd」になります (「u」は Unicode エンコーディングを意味します)。 Unicode から特定の言語に変換する場合、その言語に対応する文字がない場合は「0x3f」(「?」) が返されます。 「?」はここから来ています。
例: 文字ストリーム buf = "0x80 0x40 0xb0 0xa1" に対して new String(buf, "gb2312") 操作を実行すると、結果は "ufffdu554a" となり、それを出力すると、結果は " ?ah"、「0x80 0x40」は GB2312 ではなく GBK の文字であるためです。
別の例として、文字列 String="u00d6u00ecu00e9u0046u00bbu00f9" に対して新しい String (buf.getBytes("GBK")) 操作を実行すると、結果は "3fa8aca8a6463fa8b4" になります。 "u00d6 "「GBK」には対応する文字がないため、「3f」が得られ、「u00ec」は「a8ac」に対応し、「u00e9」は「a8a6」に対応し、「0046」は「46」に対応します(これは ASCII 文字であるため)、「u00bb」は見つかりませんでしたが、最終的に「3f」が取得されました。「u00f9」は「a8b4」に対応します。この文字列を出力すると、結果は「?ìéF?ù」になります。それを見ましたか? GBK と Unicode の間でマッピングされたコンテンツには漢字以外の文字も含まれているため、これはすべて疑問符ではありません。この例が最良の証拠です。
したがって、漢字をトランスコードするときに混乱が生じた場合、必ずしも疑問符が発生するとは限りません。しかし、結局のところ、間違いは間違いです。50 歩と 100 歩の間には質的な違いはありません。
または、「ソース文字セットには含まれていても Unicode には含まれていない場合、結果はどうなるでしょうか?」と尋ねることもできます。答えはわかりません。このテストを行うためのソース キャラクタ セットが手元にないからです。しかし、確かなことが 1 つあります。それは、ソース文字セットが十分に標準化されていないということです。 Java では、これが発生すると例外がスローされます。
UTFとは
UTFとはUnicode Text Formatの略で、Unicodeのテキスト形式を意味します。 UTF の場合、次のように定義されます。
(1) Unicode 16 ビット文字の最初の 9 ビットが 0 の場合、このバイトの最初のビットは「0」で、残りの 7 ビットはバイトで表されます。最後の 7 桁は同じです。たとえば、「u0034」(0000 0000 0011 0100) で表されます (ソース Unicode 文字と同じ)
。 2) Unicode の 16 ビット文字の最初の 5 ビットが 0 の場合、最初のバイトは「110」で始まり、次の 5 ビットはソースの上位 5 ビットと同じになります。最初の 5 つのゼロを除いた文字、2 番目のバイトは「10」で始まります。最初の次の 6 ビットは、ソース文字の下位 6 ビットと同じです。たとえば、「u025d」(0000 0010 0101 1101) は「c99d」(1100 1001 1001 1101) に変換されます。
(3) 上記 2 つの規則を満たさない場合は、3 バイトで表されます。最初のバイトは「1110」で始まり、最後の 4 ビットはソース文字の上位 4 ビットであり、2 番目のバイトは「10」で始まり、最後の 6 ビットは 3 番目のソース文字の中間 6 ビットです。バイトは「10」で始まり、最後の 6 桁はソース文字の下 6 桁になります。たとえば、「u9da7」(1001 1101 1010 0111) は「e9b6a7」(1110 1001 1011) に変換されます。 0110 1010 0111);
Unicode と Java プログラムの Unicode の違いは次のように説明できます UTF の関係は絶対的なものではありません。文字列がメモリ内で実行されるときは Unicode コードとして表示され、ファイルに保存されるときは Unicode コードとして表示されます。または他のメディアでは UTF が使用されます。この変換処理はwriteUTFとreadUTFによって完了します。
さて、基本的な議論はほぼ終わったので、本題に入りましょう。
まず問題をブラックボックスとして考えてください。まずブラック ボックスの第 1 レベルの表現を見てみましょう。
input(charsetA)->process(Unicode)->output(charsetB)
は単純な IPO モデル、つまり入力、処理、出力です。同じコンテンツを charsetA から Unicode に変換してから、charsetB に変換する必要があります。
二次表現を見てみましょう:
SourceFile(jsp,java)->class->output
この図では、入力が jsp および java ソース ファイルであることがわかります。処理中に、クラス ファイルがキャリアとして使用されます。そして出力します。次に、それを 3 番目のレベルに調整します:
jsp->temp file->class->browser,os console,db
app,servlet->class->browser,os console,db
この図はより明確になります。 Jsp ファイルは、最初に中間 Java ファイルを生成し、次にクラスを生成します。サーブレットや通常のアプリは直接コンパイルしてクラスを生成します。そして、Classからブラウザやコンソール、データベースなどに出力します。
JSP:ソースファイルからクラスまでの処理
Jspのソースファイルは「.jsp」で終わるテキストファイルです。このセクションでは、JSP ファイルの解釈とコンパイルのプロセスを説明し、中国語の変更を追跡します。
1. JSP/サーブレットエンジンが提供する JSP 変換ツール(jspc)は、JSP ファイル内の <%@ page contentType ="text/html; charset=<Jsp-charset>"%> で指定された文字セットを検索します。 JSP ファイルで <Jsp-charset> が指定されていない場合、JVM のデフォルト設定 file.encoding が使用されます。通常の状況では、この値は ISO8859-1 です。jspc
は「javac –encoding <Jsp」と同等の値を使用します。 -charset> " コマンドは、JSP ファイルに含まれるすべての文字 (中国語文字や ASCII 文字を含む) を解釈し、これらの文字を Unicode 文字に変換し、さらに UTF 形式に変換して、JAVA ファイルとして保存します。 ASCII 文字を Unicode 文字に変換する場合は、「A」のように先頭に「00」を追加するだけで、「u0041」に変換されます (理由は必要ありません。これが Unicode コード テーブルのコンパイル方法です)。そして、UTFに変換すると「41」に戻りました!これが、JSP によって生成された JAVA ファイルを表示するために通常のテキスト エディタを使用できる理由です。
3. エンジンは、「javac -encoding UNICODE」と同等のコマンドを使用して、まず
Java ファイルを CLASS ファイルにコンパイルします。
これらのプロセスの変換状況。次のソース コードがあります:
<%@ page contentType="text/html; charset=gb2312"%>
<html><本文>
<%
文字列 a="中国語";
out.println(a);
%>
</body></html>
このコードは、UltraEdit for Windows で作成されました。保存後の 2 文字「中国語」の 16 進エンコードは「D6 D0 CE C4」(GB2312 エンコード) になります。テーブルを調べると、「中国語」という単語の Unicode エンコードは「u4E2Du6587」で、UTF では「E4 B8 AD E6 96 87」であることがわかります。エンジンによって生成された JSP ファイルから変換された JAVA ファイルを開くと、「中国語」という単語が確かに「E4 B8 AD E6 96 87」に置き換えられていることがわかります。次に、JAVA ファイルのコンパイルによって生成された CLASS ファイルを確認して、見つけます。結果が JAVA ファイル内とまったく同じであることを確認します。
JSPで指定したCharSetがISO-8859-1の場合を見てみましょう。
<%@ ページ contentType="text/html; charset=ISO-8859-1"%>
<html><本文>
<%
文字列 a="中国語";
out.println(a);
%>
</body></html>
同様に、このファイルは UltraEdit で作成されており、「中国語」の 2 文字も GB2312 エンコード「D6 D0 CE C4」として保存されています。まず、JAVA ファイルと CLASS ファイルを生成するプロセスをシミュレートします。jspc は ISO-8859-1 を使用して「中国語」を解釈し、それを Unicode にマップします。 ISO-8859-1 は 8 ビットでラテン語であるため、マッピング ルールは各バイトの前に「00」を追加することになっているため、マッピングされた Unicode エンコードは、 UTFでは「C3 96 C3 90 C3 8E C3 84」である必要があります。ファイルを開いて見てください。JAVA ファイルと CLASS ファイルでは、「中国語」は確かに「C3 96 C3 90 C3 8E C3 84」と表現されています。
上記のコードで <Jsp-charset> が指定されていない場合、つまり最初の行が "<%@ page contentType="text/html" %>" として記述されている場合、JSPC は file.encoding 設定を使用して、 JSPファイル。 RedHat 6.2 では、処理結果は ISO-8859-1 を指定した場合とまったく同じになります。
ここまで、JSPファイルからCLASSファイルへの変換処理における漢字のマッピング処理について説明しました。一言で言えば、「JspCharSet から Unicode、そして UTF」までです。次の表は、このプロセスをまとめたものです。
表 2 JSP から CLASS への「中国語」変換プロセス
Jsp-CharSet | JSP ファイル内 | JAVA ファイル | 内 CLASS ファイル内 |
GB2312 | D6 D0 CE C4 (GB2312) | u4E2Du6587 (Unicode) から E4 B8 AD E6 96 87 (UTF) | E4 B8 AD E6 96 87 (UTF) |
ISO-8859 -1 | D6 D0 CE C4 (GB2312) | u00D6u00D0u00CEu00C4 (Unicode) から C3 96 C3 90 C3 8E C3 84 (UTF) | C3 96 C3 90 C3 8E C3 84 (UTF) |
なし (デフォルト = file.encoding) | ISO- と同じ8859 -1 | ISO-8859-1 と同じ | ISO-8859-1 と同じ |
サーブレット:ソースファイルからクラスまでの処理。
サーブレットのソースファイルは「.java」で終わるテキストファイルです。このセクションでは、サーブレットのコンパイル プロセスについて説明し、中国語の変更を追跡します。
「javac」を使用してサーブレットソースファイルをコンパイルします。 javac は「-encoding <Compile-charset>」パラメータを取ることができます。これは、「<Compile-charset> で指定されたエンコーディングを使用して Serlvet ソース ファイルを解釈する」ことを意味します。
ソース ファイルをコンパイルするときは、<Compile-charset> を使用して、漢字や ASCII 文字を含むすべての文字を解釈します。次に、文字定数を Unicode 文字に変換し、最後に Unicode を UTF に変換します。
サーブレットには、出力ストリームの CharSet を設定する別の場所があります。通常、結果を出力する前に、HttpServletResponse の setContentType メソッドが呼び出され、JSP で <Jsp-charset> を設定するのと同じ効果が得られます。これは <Servlet-charset> と呼ばれます。
この記事では、<Jsp-charset>、<Compile-charset>、および <Servlet-charset> という合計 3 つの変数について言及していることに注意してください。このうち、JSP ファイルは <Jsp-charset> のみに関連し、<Compile-charset> と <Servlet-charset> は Servlet のみに関連します。
次の例を見てください。
import javax.servlet.*;
import
javax.servlet.http.*;
{
public void doGet(HttpServletRequest req,HttpServletResponse resp)
ServletException、java.io.IOExceptionをスローします
{
resp.setContentType("text/html; charset=GB2312");
java.io.PrintWriter out=resp.getWriter();
out.println("<html>");
out.println("#中文#");
out.println("</html>");
}
、
「中国語」の 2 文字は「D6 D0 CE C4」(GB2312 エンコード) として保存されます。
コンパイルを開始します。次の表は、<Compile-charset>が異なる場合の CLASS ファイル内の単語「中国語」の 16 進コードを示しています。コンパイル中、<Servlet-charset> は効果がありません。 <Servlet-charset> は CLASS ファイルの出力にのみ影響します。実際、<Servlet-charset> と <Compile-charset> は連携して、JSP ファイル内の <Jsp-charset> と同じ効果を実現します。 charset >CLASS ファイルのコンパイルと出力に影響します。
表 3 「中国語」のサーブレットソースファイルからクラスへの変換プロセス
Compile-charset | サーブレット ソース ファイルの | クラス ファイルに | ある同等の Unicode コード | は
GB2312 | D6 D0 CE C4 | ||
(GB2312) | E4 B8 AD E6 96 87 (UTF) | u4E2Du6587 (Unicode = "中国語") | |
ISO-8859-1 | D6 D0 CE C4 (GB2312) | C3 96 C3 90 C3 8E C3 84 (UTF) | u00D6 u00D0 u00CE u00C4 (D6 D0 CE C4 の前に 00 が追加されます) |
なし (デフォルト) | D6 D0 CE C4 (GB2312) | ISO- と同じ8859 -1 | ISO-8859-1 と同じ |
No. | 手順 説明 | 結果 | |
1 | JSP ソースファイルを書き込み、GB2312 形式で保存 | D6 D0 CE C4 (D6D0=中文 CEC4=文) | |
2 | jspc は、JSP ソース ファイルを一時 JAVA ファイルに変換し、GB2312 に従って文字列を Unicode にマップし、それを UTF 形式で JAVA ファイルに書き込みます | E4 B8 AD E6 96 87 | |
3 | 一時ファイルをコンパイルしますJAVA ファイルを CLASS ファイルに挿入します。 | E4 B8 AD E6 96 87 | |
4 | 実行時には、まず readUTF を使用して CLASS ファイルから文字列を読み取ります。メモリ内の Unicode エンコードは | 4E 2D 65 87 (Unicode では 4E2D=中文6587=文) | |
です | 。 | Jsp -charset=GB2312 Unicode をバイト ストリームに変換 | D6 D0 CE C4 |
6 | バイト ストリームを IE に出力し、IE のエンコードを GB2312 に設定します (著者注: この情報は HTTP ヘッダーに隠されています) | D6 D0 CE C4 | |
7 | IE View結果「中国語」と | 「簡体字中国語」(正しい表示) |
No. | 手順 説明 | 結果 | |
1 | JSP ソースファイルを書き込み、GB2312 形式で保存 | D6 D0 CE C4 (D6D0=中文 CEC4=文) | |
2 | jspc は、JSP ソース ファイルを一時 JAVA ファイルに変換し、ISO8859-1 に従って文字列を Unicode にマップし、それを UTF 形式で JAVA ファイルに書き込みます | C3 96 C3 90 C3 8E C3 | |
84 | 3 | 一時的な JAVA ファイルが CLASS ファイルにコンパイルされます。 | C3 96 C3 90 C3 8E C3 84 |
4. | 実行時には、まず readUTF を使用して CLASS ファイルから文字列を読み取ります。メモリ内の Unicode エンコードは00 D6 00 D0 00 CE 00 C4 | です | 。(何もなし!!!) |
5 | Jsp-charset=ISO8859-1 に従ってUnicode をバイト ストリーム | D6 D0 CE C4 | に変換します。|
6 | バイト ストリームを IE に出力し、IE のエンコーディングを ISO8859-1 に設定します (著者プレス: この情報は次のとおりです)。 HTTP ヘッダーに隠されています) | D6 D0 CE C4 | |
7 | IE では「西ヨーロッパ文字」を使用して | 文字化けとして表示されます。実際には 4 つの ASCII 文字ですが、128 を超えているため、表示がおかしくなります。 | |
8 | ページのエンコードを変更します。 IEの「簡体字中国語」「中文」 | 「中文」(正しい表示) |
No. | 手順 説明 | 結果 | |
1 | JSP ソースファイルを書き込み、GB2312 形式で保存 | D6 D0 CE C4 (D6D0=中文 CEC4=文) | |
2 | jspc は、JSP ソース ファイルを一時 JAVA ファイルに変換し、ISO8859-1 に従って文字列を Unicode にマップし、それを UTF 形式で JAVA ファイルに書き込みます | C3 96 C3 90 C3 8E C3 | |
84 | 3 | 一時的な JAVA ファイルが CLASS ファイルにコンパイルされます。 | C3 96 C3 90 C3 8E C3 84 |
4. | 実行時には、まず readUTF を使用して CLASS ファイルから文字列を読み取ります。メモリ内の Unicode エンコードは00 D6 00 D0 00 CE 00 C4 | です | 。|
5. | Jsp-charset=ISO8859-1 に従って、Unicode をバイト ストリームに変換します | D6 D0 CE C4 | |
6 | バイト ストリームを IE に出力します | D6 D0 CE C4 | |
7 | IE は、結果を表示するリクエストが行われたときにページのエンコーディングを使用します。 | 状況に応じて。簡体字中国語の場合は正しく表示できます。それ以外の場合は、表 5 の手順 8 を実行する必要があります。 |
No. | ステップ 説明 | 結果 |
1 | サーブレットソースファイルを書き込み、GB2312 形式で保存します | D6 D0 CE C4 (D6D0=中国語 CEC4=中国語) |
2 | javac –encoding GB2312 を使用して、JAVA ソース ファイルを CLASS ファイルにコンパイルします | E4 B8 AD E6 96 87 (UTF) |
3 | 実行時に、最初に readUTF を使用して CLASS ファイルから文字列を読み取り、保存しますエンコーディングは Unicode | 4E 2D 65 87 (Unicode) | です。
4 | Servlet-charset=GB2312 に従って、Unicode をバイト ストリーム | D6 D0 CE C4 (GB2312) | に変換します。
5 | バイト ストリームを IE に出力し、IE のエンコーディング属性を次のように設定します。 Servlet- charset=GB2312 | D6 D0 CE C4 (GB2312) |
6 | IE は「簡体字中国語」を使用して結果 | 「中国語」を表示します (正しく表示されます) |
No. | ステップ 説明 | 結果 |
1 | サーブレットソースファイルを書き込み、GB2312 形式で保存します | D6 D0 CE C4 (D6D0=中文 CEC4=文) |
2 | javac –encoding ISO8859-1 を使用して、JAVA ソース ファイルを CLASS ファイル | C3 96 C3 90 C3 8E C3 84 (UTF) | にコンパイルします。
3 | 実行時には、まず次のコマンドを使用して CLASS ファイルから文字列を読み取ります。 readUTF 、メモリ内の Unicode エンコーディングは | 00 D6 00 D0 00 CE 00 C4 |
4 | Servlet-charset=ISO8859-1 に従って Unicode をバイト ストリームに変換します | D6 D0 CE C4 |
5 | バイト ストリームを IE に出力し、IE のエンコーディングを設定します属性は Servlet-charset=ISO8859-1 | D6 D0 CE C4 (GB2312) | です。
6 | IE は文字化けした結果を表示するために「西欧文字」を使用します | (理由は表 5 と同じです)。 |
7 | IE のページ エンコーディングを「簡体字中国語」に変更します。 " | "中国語" (正しく表示されます) |
シリアル番号 | ステップの説明 | 結果 | フィールド | |
1 | IE に「中国語」と入力します | D6 D0 CE C4 | IE | |
2 | IE は文字列を UTF に変換し、トランスポート ストリームに送信します | E4 B8 AD E6 96 87 | ||
3 | サーブレットは入力ストリームを受信し、readUTF | 4E | で読み取ります2D 65 87 (unicode) | サーブレット |
4 | サーブレットでは、プログラマは GB2312 に従って文字列をバイト ストリーム | D6 D0 CE C4 | に復元する必要があります。||
5 | プログラマはデータベース内部コード ISO8859 に従って新しい文字列 | 00 D6 00 D0 00 CE | を生成します||
00 C4 | ||||
6 | 新しく生成された文字列を JDBC に送信します | 。 | ||
7 | JDBC は、データベースの内部コードが ISO8859-1 であることを検出します。 | 00 D6 00 D0 00 CE 00 C4 | JDBC | |
8 | JDBC は、受信した文字列を変換します。 ISO8859 に準拠 -1 バイト ストリームの生成 | D6 D0 CE C4 | ||
9 | JDBC がバイト ストリームをデータベースに書き込みます | D6 D0 CE C4 | ||
10 | データ ストレージ作業を完了します | D6 D0 CE C4 データベース | ||
以下はデータベースから数値を取得するプロセスです | ||||
11 | JDBC が取得しますデータベースからの単語 Throttle | D6 D0 CE C4 | JDBC | |
12 | JDBC はデータベース文字セット ISO8859-1 に従って文字列を生成し、それをサーブレットに送信します | 00 D6 00 D0 00 CE 00 C4 (Unicode) | ||
13 | サーブレットは文字列 | 00 D6 00 D0 00 CE 00 C4 (Unicode) | サーブレット | を取得します。|
14 | プログラマは、データベースの内部コード ISO8859-1 に従って、元のバイト ストリーム | D6 D0 CE C4 | を復元する必要があります。||
15 | プログラマはクライアントの文字セットに従って新しい文字列を生成する必要があります GB2312 | 4E 2D 65 87 (ユニコード) | ||
サーブレットは、文字列をクライアント | ||||
16 | に出力する準備をします。サーブレットは、 <Servlet-charset> に基づいてバイト ストリーム | D6D0 CE C4 | サーブレット | |
17 | を生成します。 <Servlet-charset> が指定されている場合、サーブレットはバイト ストリームを IE に出力します。 IE のバイト ストリームも設定されます。エンコーディングは <Servlet-charset> | D6 D0 CE C4 | ||
18 | IE 指定されたエンコーディングまたはデフォルトのエンコーディングIE | に従って | 結果「中国語」(正しく表示されます) | を表示します。