まず、これは Java の String を指すことを説明します。C/C++ に切り替えることにしましたが、今日問題が発生したため、まだ調べておきたいと思います。文字列の定義は次のとおりです。
次のようにコードをコピーします。
パブリック最終クラス文字列
{
private Final char value[]; // 保存された文字列
private Final int オフセット; // 開始位置;
privatefinal int count; //文字数;
private int ハッシュ // キャッシュされたハッシュ値;
...
}
デバッグ時は、次のように保存された値を確認できます。
hashCode()が呼び出されていない場合、ハッシュ値は0であることに注意してください。ここでの値が実際に保存された文字列値 (つまり、「文字列テスト」) の char 配列であることは簡単にわかります。また、各 char の値は何でしょうか。検証が簡単: Unicode。
この時点で、一般的に使用される subString がどのように実装されているかは誰もが推測できます。これを実装する場合は、新しい String に同じ値 (char 配列) を使用させ、オフセットとカウントのみを変更します。これはスペースを節約し、高速に実行できます (コピーする必要はありません)。実際には次のようになります。
次のようにコードをコピーします。
public String substring(int beginIndex) {
部分文字列を返します(beginIndex, count);
}
public String substring(int beginIndex, int endIndex) {
...
return ((beginIndex == 0) && (endIndex == count)) これは?
new String(offset + beginIndex, endIndex - beginIndex, value);
}
String(int オフセット、int カウント、char 値[]) {
this.value = 値;
this.offset = オフセット;
this.count = カウント;
}
文字列について説明しているので、JVM はデフォルトでどのようなエンコーディングを使用するのでしょうか?デバッグを通じて次のことがわかります。
次のようにコードをコピーします。
パブリック静的文字セットdefaultCharset() {
if (defaultCharset == null) {
同期化 (Charset.class) {
java.security.PrivilegedAction pa = new GetPropertyAction("file.encoding");
String csn = (String)AccessController.doPrivileged(pa);
文字セット cs = lookup(csn);
if (cs != null)
デフォルトの文字セット = cs;
それ以外
defaultCharset = forName("UTF-8");
}
}
defaultCharset の値を渡すことができます。
-Dfile.encoding=utf-8
設定を行います。もちろん、「abc」に設定したい場合は設定できますが、デフォルトでは UTF-8 に設定されます。 System.getProperty("file.encoding") を通じて特定の値を確認できます。なぜdefaultCharsetが表示されるのでしょうか?ネットワーク送信プロセスはバイト配列である必要があるため、異なるエンコード方法によって取得されるバイト配列は異なる場合があります。では、エンコード方法がどのように取得されるかを知る必要がありますよね?バイト配列を取得する具体的なメソッドは getBytes ですが、これは最終的に次のように CharsetEncoder の encode メソッドを呼び出すことに焦点を当てます。
次のようにコードをコピーします。
public Final CoderResult encode(CharBuffer in, ByteBuffer out, boolean endOfInput) {
int newState = endOfInput ST_END : ST_CODING;
if ((状態 != ST_RESET) && (状態 != ST_CODING) && !(endOfInput && (状態 == ST_END)))
throwIllegalStateException(状態, newState);
状態 = 新しい状態;
のために (;;) {
CoderResult cr;
試す {
cr = encodeLoop(in, out);
キャッチ (BufferUnderflowException x) {
新しい CoderMalfunctionError(x) をスローします。
キャッチ (BufferOverflowException x) {
新しい CoderMalfunctionError(x) をスローします。
}
if (cr.isOverflow())
crを返します。
if (cr.isUnderflow()) {
if (endOfInput && in.hasRemaining()) {
cr = CoderResult.malformedForLength(in.remaining());
} それ以外 {
crを返します。
}
}
codingErrorAction アクション = null;
if (cr.isMalformed())
アクション = malformedInputAction;
else if (cr.isUnmappable())
アクション = unmappableCharacterAction;
それ以外
false をアサートします: cr.toString();
if (アクション ==codingErrorAction.REPORT)
crを返します。
if (アクション ==codingErrorAction.REPLACE) {
if (out.remaining() < replace.length)
CoderResult.OVERFLOW を返します。
out.put(置換);
}
if ((アクション ==codingErrorAction.IGNORE) || (アクション ==codingErrorAction.REPLACE)) {
in.position(in.position() + cr.length());
続く;
}
false をアサートします。
}
}
もちろん、対応する CharsetEncoder は、必要なエンコード形式に従って最初に選択されます。最も重要なことは、異なる CharsetEncoder が異なる encodeLoop メソッドを実装することです。なぜここに for(;;) があるのか理解できないかもしれません。実際、CharsetEncoder が配置されているパッケージ (nio) とそのパラメータを見ることで、これを大まかに理解できます。この関数はストリームを処理できます (ただし、ここで使用する場合はループしません)。
encodeLoop メソッドでは、できるだけ多くの文字がバイトに変換され、新しい String は上記とほぼ逆のプロセスになります。
実際の開発プロセスでは、以下のような文字化けが発生することがよくあります。
ファイルをアップロードするときにファイル名を取得します。
JS によってバックエンドに渡される文字列。
まず、次のコードの実行結果を試してください。
次のようにコードをコピーします。
public static void main(String[] args) throws Exception {
文字列 str = "文字列";
// -41 -42 -73 -5 -76 -82
printArray(str.getBytes());
// -27 -83 -105 -25 -84 -90 -28 -72 -78
printArray(str.getBytes("utf-8"));
//?
System.out.println(new String(str.getBytes(), "utf-8"));
// インジュアン?
System.out.println(new String(str.getBytes("utf-8"), "gbk"));
//キャラクター??
System.out.println(new String("瀛涓?".getBytes("gbk"), "utf-8"));
// -41 -42 -73 -5 63 63
printArray(new String("Yingjuan?".getBytes("gbk"), "utf-8").getBytes());
}
public static void printArray(byte[] bs){
for(int i = 0; i < bs.length; i++){
System.out.print(bs[i] + " ");
}
System.out.println();
}
出力はプログラム内のコメントで説明されています。
GBK の 2 バイトは漢字を表すため、6 バイトになります。
UTF-8 では 3 バイトが漢字を表すため、9 バイトになります。
GBK で生成できないバイト配列を使用して UTF-8 の規則に従って文字列を生成するため、「???」が表示されます。
これが、文字化けが頻繁に発生する理由です。GBK は、UTF-8 によって生成されたバイトを使用して文字列を生成します。
上記で生成されたコードは文字化けしていますが、コンピュータは文字化けを認識していないため、getBytes を通じてバイト配列を取得でき、この配列内の UTF-8 は認識できます。
最後の 2 つの 63 (?) はエンコードによって埋められる必要があります (そうでないと、直接埋めるのに十分なバイトがありません。この場所を注意深く見ていませんでした)。
GBK と UTF-8 では文字と数字のエンコーディングが同じであるため、文字化けは発生しません。しかし、漢字のエンコーディングは実際には異なります。これが多くの問題の原因です。以下のコードで:
new String(new String("we".getBytes("UTF-8"), "GBK").getBytes("GBK"), "UTF-8);
このコードの結果は明らかに「私たち」ですが、それは私たちに何をもたらすのでしょうか?まず次のことに気づきます。
new String("we".getBytes("UTF-8"), "GBK");
このコードの結果はコード化けであり、コード化けの多くは「こんなふうにめちゃくちゃ」です。ただし、覚えておいてください。ここでの混乱は私たちにとってのものであり、コンピュータにとっては、それが「乱雑」か「乱雑ではない」かは関係ありません。私たちがあきらめそうになったときでも、「getBytes(」を通じて文字化けしたコードから情報を取得できるのです。 "GBK")" その "バックボーン" を使用すると、その "バックボーン" を使用して元の文字列を復元できます。
上記のコードで「GBK」と「UTF-8」の間の文字化け問題は解決できるようですが、この解決方法は、連続する漢字の数がすべて偶数であるという特殊な場合にのみ限定されます。理由は上で述べたのでここでは繰り返しません。
では、この問題をどうやって解決すればいいのでしょうか?
最初の解決策: encodeURIなぜこの方法を使用するのでしょうか?理由は非常に簡単です。GBK と UTF-8 は、%、数字、文字のエンコーディングが同じであるため、エンコーディング後の文字列は、これら 2 つのエンコーディングでは同じであることが 100% 保証され、デコードして文字を取得できます。 . 串に刺すだけ。 String の形式から、エンコードとデコードの効率が非常に高いことが推測できるため、これも良い解決策です。
2 番目の解決策: 統一エンコード形式<BR>ここでは Webx マイニングを使用しています。webx.xml でdefaultCharset="UTF-8" を設定するだけです。