أولاً، اسمحوا لي أن أوضح أن هذا يشير إلى String في Java. على الرغم من أنني قررت التبديل إلى C/C++، لأنني واجهت مشكلة اليوم، ما زلت أرغب في إلقاء نظرة. تعريف السلسلة هو كما يلي:
انسخ رمز الكود كما يلي:
سلسلة الفئة النهائية العامة
{
قيمة الحرف النهائية الخاصة []؛ // السلسلة المحفوظة
إزاحة نهائية خاصة // موضع البداية
عدد صحيح نهائي خاص // عدد الأحرف
تجزئة int الخاصة؛ // قيمة التجزئة المخزنة مؤقتًا
...
}
عند التصحيح، يمكنك رؤية القيم المحفوظة على النحو التالي:
تجدر الإشارة إلى أنه إذا لم يتم استدعاء hashCode()، فإن قيمة التجزئة هي 0. من السهل معرفة أن القيمة هنا هي مصفوفة char لقيمة السلسلة المحفوظة الفعلية (أي "اختبار السلسلة")، وما هي قيمة كل حرف؟ سهولة التحقق: Unicode.
في هذه المرحلة، يمكن للجميع تخمين كيفية تنفيذ السلسلة الفرعية شائعة الاستخدام: إذا أردنا تنفيذها، دع السلسلة الجديدة تستخدم نفس القيمة (صفيف char) وتعديل الإزاحة والعد فقط. وهذا يوفر مساحة وسريع (لا حاجة للنسخ)، وفي الواقع هو مثل هذا:
انسخ رمز الكود كما يلي:
سلسلة فرعية عامة (int beginIndex) {
إرجاع سلسلة فرعية (beginIndex، count)؛
}
سلسلة فرعية عامة (int beginIndex، int endIndex) {
...
العودة ((beginIndex == 0) && (endIndex == count)) هذا:
سلسلة جديدة (إزاحة + beginIndex، endIndex - beginIndex، value)؛
}
سلسلة (إزاحة int، عدد int، قيمة char[]) {
this.value = value;
this.offset = offset;
this.count = count;
}
بما أننا نناقش السلاسل، ما هو التشفير الذي يستخدمه JVM افتراضيًا؟ من خلال التصحيح يمكنك العثور على:
انسخ رمز الكود كما يلي:
مجموعة الأحرف الثابتة العامة defaultCharset() {
إذا (defaultCharset == null) {
متزامن (Charset.class) {
java.security.PrivilegedAction pa = new GetPropertyAction("file.encoding");
String csn = (String)AccessController.doPrivileged(pa);
مجموعة الأحرف cs = lookup(csn);
إذا (CS ! = فارغة)
defaultCharset = cs;
آخر
defaultCharset = forName("UTF-8");
}
}
يمكن تمرير قيمة defaultCharset:
-Dfile.encoding=utf-8
قم بإجراء الإعدادات. بالطبع، إذا كنت تريد تعيينه على "abc"، فيمكنك ذلك، ولكن سيتم تعيينه على UTF-8 افتراضيًا. يمكنك رؤية القيمة المحددة من خلال System.getProperty("file.encoding"). لماذا ترى defaultCharset؟ نظرًا لأن عملية نقل الشبكة يجب أن تكون عبارة عن صفائف بايت، فقد تكون صفائف البايت التي تم الحصول عليها بطرق تشفير مختلفة مختلفة. لذلك، نحن بحاجة إلى معرفة كيفية الحصول على طريقة التشفير، أليس كذلك؟ الطريقة المحددة للحصول على مصفوفة البايت هي getBytes، والتي سنركز عليها أدناه، وما يطلق عليه في النهاية هو طريقة التشفير الخاصة بـ CharsetEncoder، كما يلي:
انسخ رمز الكود كما يلي:
تشفير CoderResult النهائي العام (CharBuffer in، ByteBuffer out، boolean endOfInput) {
int newState = endOfInput ST_END : ST_CODING;
إذا ((الحالة != ST_RESET) && (الحالة != ST_CODING) && !(endOfInput && (الحالة == ST_END)))
throwIllegalStateException(state, newState);
State = newState;
ل (؛؛) {
CoderResult كر;
يحاول {
cr = encodeLoop(in, out);
} التقاط (BufferUnderflowException x) {
رمي CoderMalfunctionError جديد (x)؛
} التقاط (BufferOverflowException x) {
رمي CoderMalfunctionError جديد (x)؛
}
إذا (cr.isOverflow ())
العودة كر.
إذا (cr.isUnderflow()) {
إذا (endOfInput && in.hasRemaining()) {
cr = CoderResult.malformedForLength(in.remaining());
} آخر {
العودة كر.
}
}
CodingErrorAction action = null;
إذا (cr.isMalformed ())
action = malformedInputAction;
وإلا إذا (cr.isUnmappable())
action = unmappableCharacterAction;
آخر
تأكيد خطأ: cr.toString();
إذا (الإجراء == CodingErrorAction.REPORT)
العودة كر.
إذا (الإجراء == CodingErrorAction.REPLACE) {
إذا (out.remaining() <replacement.length)
إرجاع CoderResult.OVERFLOW;
out.put(replacement);
}
إذا ((الإجراء == CodingErrorAction.IGNORE) || (الإجراء == CodingErrorAction.REPLACE)) {
in.position(in.position() + cr.length());
يكمل؛
}
تأكيد كاذب؛
}
}
بالطبع، سيتم تحديد CharsetEncoder المقابل أولاً وفقًا لتنسيق التشفير المطلوب، والأهم هو أن CharsetEncoder المختلف ينفذ طرق encodeLoop مختلفة. قد لا تفهم سبب وجود for(;;) هنا؟ في الواقع، يمكنك فهمها تقريبًا من خلال النظر إلى الحزمة (nio) التي يوجد بها CharsetEncoder ومعلماتها: يمكن لهذه الوظيفة التعامل مع التدفقات (على الرغم من أننا لن نكرر عندما نستخدمها هنا).
في طريقة encodeLoop، سيتم تحويل أكبر عدد ممكن من الأحرف إلى بايت، والسلسلة الجديدة تقريبًا هي العملية العكسية المذكورة أعلاه.
في عملية التطوير الفعلية، غالبًا ما تتم مواجهة أحرف مشوهة:
احصل على اسم الملف عند تحميل الملف؛
السلسلة التي تم تمريرها بواسطة JS إلى الواجهة الخلفية؛
قم أولاً بتجربة نتائج تشغيل الكود التالي:
انسخ رمز الكود كما يلي:
public static void main(String[] args) يطرح الاستثناء {
سلسلة سلسلة = "سلسلة"؛
// -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());
}
طباعة الفراغ الثابت العام (بايت [] بكالوريوس) {
ل(int i = 0; i < bs.length; i++){
System.out.print(bs[i] + " ");
}
System.out.println();
}
تم وصف الإخراج في التعليقات في البرنامج:
لأن 2 بايت في GBK تمثل حرفًا صينيًا، هناك 6 بايت؛
نظرًا لأن 3 بايتات في UTF-8 تمثل حرفًا صينيًا، فهناك 9 بايت؛
نظرًا لاستخدام مصفوفة البايت التي لا يمكن إنشاؤها بواسطة GBK لإنشاء سلسلة وفقًا لقواعد UTF-8، يتم عرض ؟؟؟
هذا هو السبب وراء ظهور أحرف مشوهة في كثير من الأحيان. يستخدم GBK البايت الذي تم إنشاؤه بواسطة UTF-8 لإنشاء سلاسل؛
على الرغم من أن الكود الذي تم إنشاؤه أعلاه مشوه، إلا أن الكمبيوتر لا يعتقد ذلك، لذلك لا يزال بإمكانه الحصول على مصفوفة البايت من خلال getBytes، ويمكن التعرف على UTF-8 في هذه المصفوفة؛
يجب ملء آخر 63 (؟) بالتشفير (أو لا توجد وحدات بايت كافية لملءها مباشرة، ولم ألقي نظرة فاحصة على هذا المكان)؛
نظرًا لأن تشفير الحروف والأرقام هو نفسه بين GBK وUTF-8، فلن تكون هناك أحرف مشوهة في معالجة هذه الأحرف، ومع ذلك، فإن ترميز الأحرف الصينية مختلف بالفعل، وهذا هو أصل العديد من المشاكل على الكود أدناه:
new String(new String("we".getBytes("UTF-8"), "GBK").getBytes("GBK"), "UTF-8);
من الواضح أن نتيجة هذا الرمز هي "نحن"، ولكن ماذا يفعل بنا؟ أولا نلاحظ:
سلسلة جديدة("نحن".getBytes("UTF-8"), "GBK");
نتيجة هذا الرمز هي كود مشوه، والعديد من الرموز المشوهة "مفسدة بهذه الطريقة". لكن تذكر: الفوضى هنا من أجلنا، وبالنسبة للكمبيوتر، لا يهم ما إذا كانت "فوضوية" أو "غير فوضوية". وعندما نستسلم تقريبًا، لا يزال بإمكانه الحصول عليها من التعليمات البرمجية المشوهة من خلال "getBytes(". "GBK")" إنه "العمود الفقري"، وبعد ذلك يمكننا استخدام "العمود الفقري" لاستعادة السلسلة الأصلية.
يبدو أن الكود أعلاه يمكنه حل المشكلة المشوهة بين "GBK" و"UTF-8"، لكن هذا الحل يقتصر فقط على حالة خاصة: عدد جميع الأحرف الصينية المتتالية هو رقم زوجي! وقد ذكرت الأسباب أعلاه ولن أكررها هنا.
فكيف حل هذه المشكلة؟
الحل الأول: encodeURI لماذا استخدام هذه الطريقة؟ السبب بسيط للغاية: GBK وUTF-8 لهما نفس التشفير بنسبة % وأرقام وحروف، لذلك يمكن ضمان أن تكون السلسلة بعد التشفير هي نفس الشيء بنسبة 100% تحت هذين التشفيرين، ثم فك التشفير للحصول على الأحرف سيخ فقط . وفقًا لتنسيق السلسلة، يمكننا تخمين أن كفاءة التشفير وفك التشفير عالية جدًا، لذا يعد هذا أيضًا حلاً جيدًا.
الحل الثاني: تنسيق التشفير الموحد <BR>نحن نستخدم تعدين Webx هنا، ما عليك سوى تعيين defaultCharset="UTF-8" في webx.xml.