Lassen Sie mich zunächst erklären, dass sich dies auf String in Java bezieht. Obwohl ich mich entschieden habe, auf C/C++ umzusteigen, möchte ich trotzdem einen Blick darauf werfen, da ich heute auf ein Problem gestoßen bin. Die Definition von String lautet wie folgt:
Kopieren Sie den Codecode wie folgt:
öffentlicher finaler Klassenstring
{
privater endgültiger Zeichenwert[]; // gespeicherter String
private final int offset; // Startposition
private final int count; //Anzahl der Zeichen
privater int hash; // zwischengespeicherter Hashwert
...
}
Beim Debuggen können Sie die gespeicherten Werte wie folgt sehen:
Es ist zu beachten, dass der Hash-Wert 0 ist, wenn hashCode() nicht aufgerufen wurde. Es ist leicht zu erkennen, dass der Wert hier das Char-Array des tatsächlich gespeicherten String-Werts ist (dh der „String-Test“). Wie hoch ist der Wert jedes Zeichens? Einfach zu überprüfen: Unicode.
An diesem Punkt kann jeder erraten, wie unser häufig verwendeter Teilstring implementiert wird: Wenn wir ihn implementieren würden, würde der neue String denselben Wert (Char-Array) verwenden und nur den Offset und die Anzahl ändern. Das spart Platz und ist schnell (kein Kopieren nötig), und tatsächlich sieht es so aus:
Kopieren Sie den Codecode wie folgt:
public String substring(int beginIndex) {
return substring(beginIndex, count);
}
public String substring(int beginIndex, int endIndex) {
...
return ((beginIndex == 0) && (endIndex == count)) ?
new String(offset + beginIndex, endIndex - beginIndex, value);
}
String(int offset, int count, char value[]) {
this.value = value;
this.offset = offset;
this.count = count;
}
Welche Codierung verwendet die JVM standardmäßig, da wir über Zeichenfolgen sprechen? Durch Debuggen können Sie Folgendes finden:
Kopieren Sie den Codecode wie folgt:
öffentlicher statischer Zeichensatz defaultCharset() {
if (defaultCharset == null) {
synchronisiert (Charset.class) {
java.security.PrivilegedAction pa = new GetPropertyAction("file.encoding");
String csn = (String)AccessController.doPrivileged(pa);
Charset cs = lookup(csn);
if (cs != null)
defaultCharset = cs;
anders
defaultCharset = forName("UTF-8");
}
}
Der Wert von defaultCharset kann übergeben werden:
-Dfile.encoding=utf-8
Nehmen Sie Einstellungen vor. Wenn Sie es auf „abc“ setzen möchten, können Sie das natürlich tun, aber es wird standardmäßig auf UTF-8 eingestellt. Sie können den spezifischen Wert über System.getProperty("file.encoding") sehen. Warum wird defaultCharset angezeigt? Da der Netzwerkübertragungsprozess ein Byte-Array sein sollte, können die durch verschiedene Codierungsmethoden erhaltenen Byte-Arrays unterschiedlich sein. Wir müssen also wissen, wie die Kodierungsmethode erhalten wird, oder? Die spezifische Methode zum Abrufen des Byte-Arrays ist getBytes, auf die wir uns im Folgenden konzentrieren werden. Letztendlich wird die Methode encode von CharsetEncoder wie folgt aufgerufen:
Kopieren Sie den Codecode wie folgt:
public final CoderResult encode(CharBuffer in, ByteBuffer out, boolean endOfInput) {
int newState = endOfInput ? ST_END : ST_CODING;
if ((state != ST_RESET) && (state != ST_CODING) && !(endOfInput && (state == ST_END)))
throwIllegalStateException(state, newState);
state = newState;
für (;;) {
CoderResult cr;
versuchen {
cr = encodeLoop(in, out);
} Catch (BufferUnderflowException x) {
throw new CoderMalfunctionError(x);
} Catch (BufferOverflowException x) {
throw new CoderMalfunctionError(x);
}
if (cr.isOverflow())
Rückkehr cr;
if (cr.isUnderflow()) {
if (endOfInput && in.hasRemaining()) {
cr = CoderResult.malformedForLength(in.remaining());
} anders {
Rückkehr cr;
}
}
CodingErrorAction action = null;
if (cr.isMalformed())
action = malformedInputAction;
sonst wenn (cr.isUnmappable())
action = unmappableCharacterAction;
anders
behaupten falsch: cr.toString();
if (action == CodingErrorAction.REPORT)
Rückkehr cr;
if (action == CodingErrorAction.REPLACE) {
if (out.remaining() < replacement.length)
return CoderResult.OVERFLOW;
out.put(Ersatz);
}
if ((action == CodingErrorAction.IGNORE) || (action == CodingErrorAction.REPLACE)) {
in.position(in.position() + cr.length());
weitermachen;
}
behaupten falsch;
}
}
Natürlich wird zuerst der entsprechende CharsetEncoder entsprechend dem erforderlichen Codierungsformat ausgewählt. Das Wichtigste ist, dass unterschiedliche CharsetEncoder unterschiedliche encodeLoop-Methoden implementieren. Sie verstehen vielleicht nicht, warum es hier ein for(;;) gibt? Tatsächlich können Sie es grob verstehen, indem Sie sich das Paket (nio) ansehen, in dem sich CharsetEncoder befindet, und seine Parameter: Diese Funktion kann Streams verarbeiten (obwohl wir sie hier nicht in einer Schleife verwenden).
In der encodeLoop-Methode werden so viele Zeichen wie möglich in Bytes umgewandelt, und der neue String ist fast der umgekehrte Vorgang wie oben.
Im eigentlichen Entwicklungsprozess werden häufig verstümmelte Zeichen angetroffen:
Rufen Sie beim Hochladen der Datei den Dateinamen ab.
Die von JS an das Backend übergebene Zeichenfolge;
Probieren Sie zunächst die laufenden Ergebnisse des folgenden Codes aus:
Kopieren Sie den Codecode wie folgt:
public static void main(String[] args) löst eine Ausnahme aus {
String str = "string";
// -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"));
// Yingjuan?
System.out.println(new String(str.getBytes("utf-8"), "gbk"));
//Charakter??
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();
}
Die Ausgabe wird in den Kommentaren im Programm beschrieben:
Da 2 Bytes in GBK ein chinesisches Zeichen darstellen, sind es 6 Bytes;
Da 3 Bytes in UTF-8 ein chinesisches Zeichen darstellen, sind es 9 Bytes;
Da das Byte-Array, das von GBK nicht generiert werden kann, zum Generieren einer Zeichenfolge gemäß den UTF-8-Regeln verwendet wird, wird ??? angezeigt.
Aus diesem Grund treten häufig verstümmelte Zeichen auf. GBK verwendet das von UTF-8 generierte Byte, um Zeichenfolgen zu generieren.
Obwohl der oben generierte Code verstümmelt ist, glaubt der Computer nicht daran, sodass er das Byte-Array dennoch über getBytes abrufen und UTF-8 in diesem Array erkennen kann.
Die letzten beiden 63 (?) sollten durch Codierung gefüllt werden (oder es sind nicht genügend Bytes zum direkten Füllen vorhanden, ich habe mir diese Stelle nicht genau angesehen);
Da die Kodierung von Buchstaben und Zahlen zwischen GBK und UTF-8 gleich ist, treten bei der Verarbeitung dieser Zeichen keine verstümmelten Zeichen auf. Die Kodierung chinesischer Zeichen ist jedoch tatsächlich unterschiedlich unter folgendem Code:
new String(new String("we".getBytes("UTF-8"), "GBK").getBytes("GBK"), "UTF-8);
Offensichtlich ist das Ergebnis dieses Codes „wir“, aber was macht er mit uns? Zuerst bemerken wir:
new String("we".getBytes("UTF-8"), "GBK");
Das Ergebnis dieses Codes ist verstümmelter Code, und viele verstümmelte Codes sind „so durcheinander“. Aber denken Sie daran: Das Chaos hier ist für uns, und für den Computer spielt es keine Rolle, ob es „chaotisch“ oder „nicht chaotisch“ ist. Wenn wir fast aufgeben, kann er es immer noch aus dem verstümmelten Code durch „getBytes(“ herausholen. „GBK“)“ Es ist „Rückgrat“, und dann können wir das „Rückgrat“ verwenden, um die ursprüngliche Zeichenfolge wiederherzustellen.
Es scheint, dass der obige Code das verstümmelte Problem zwischen „GBK“ und „UTF-8“ lösen kann, aber diese Lösung ist nur auf einen Sonderfall beschränkt: Die Anzahl aller aufeinanderfolgenden chinesischen Zeichen ist eine gerade Zahl! Die Gründe wurden oben genannt und werden hier nicht wiederholt.
Wie kann man dieses Problem lösen?
Die erste Lösung: encodeURI Warum diese Methode verwenden? Der Grund ist sehr einfach: GBK und UTF-8 haben die gleiche Codierung von %, Zahlen und Buchstaben, sodass die Zeichenfolge nach der Codierung unter diesen beiden Codierungen zu 100 % garantiert identisch ist und dann decodiert wird, um die Zeichen zu erhalten . Einfach aufspießen. Anhand des String-Formats können wir davon ausgehen, dass die Effizienz der Codierung und Decodierung sehr, sehr hoch ist, sodass dies auch eine gute Lösung ist.
Die zweite Lösung: Einheitliches Codierungsformat <BR>Wir verwenden hier Webx-Mining. Sie müssen nur defaultCharset="UTF-8" in webx.xml festlegen.