Прежде всего, позвольте мне объяснить, что это относится к String в Java. Хотя я решил переключиться на C/C++, поскольку сегодня столкнулся с проблемой, я все равно хочу взглянуть. Определение строки следующее:
Скопируйте код кода следующим образом:
публичный финальный класс String
{
частное окончательное значение символа [] // сохраненная строка
Private Final int offset // начальная позиция;
частный окончательный int count //Количество символов;
частный int hash // кэшированное значение хеш-функции
...
}
При отладке увидеть сохраненные значения можно следующим образом:
Следует отметить, что если hashCode() не вызывалась, значение хеш-функции равно 0. Легко понять, что значение здесь представляет собой массив символов фактического сохраненного строкового значения (то есть «строкового теста»), и каково значение каждого символа? Легко проверить: Юникод.
На этом этапе каждый может догадаться, как реализована наша часто используемая подстрока: если бы мы ее реализовали, пусть новая строка использует то же значение (массив символов) и изменяет только смещение и счетчик. Это экономит место и работает быстро (не нужно копировать), а по сути это так:
Скопируйте код кода следующим образом:
подстрока публичной строки (int BeginIndex) {
вернуть подстроку (beginIndex, count);
}
подстрока публичной строки (int BeginIndex, int endIndex) {
...
return ((beginIndex == 0) && (endIndex == count)) это:
новая строка (смещение + BeginIndex, endIndex - BeginIndex, значение);
}
String(int offset, int count, char value[]) {
это.значение = значение;
this.offset = смещение;
this.count = количество;
}
Поскольку мы обсуждаем строки, какую кодировку JVM использует по умолчанию? С помощью отладки вы можете найти:
Скопируйте код кода следующим образом:
общественная статическая кодировка defaultCharset() {
если (defaultCharset == null) {
синхронизировано (Charset.class) {
java.security.PrivilegedAction pa = новое GetPropertyAction("file.encoding");
String csn = (String)AccessController.doPrivileged(pa);
Кодировка cs = поиск(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, логическое значение endOfInput) {
int newState = endOfInput? ST_END: ST_CODING;
if ((state != ST_RESET) && (state != ST_CODING) && !(endOfInput && (state == ST_END)))
throwIllegalStateException (состояние, newState);
состояние = новое состояние;
для (;;) {
КодерРезультат кр;
пытаться {
cr = encodeLoop (вход, выход);
} catch (BufferUnderflowException x) {
выбросить новую CoderMalfunctionError(x);
} catch (BufferOverflowException x) {
выбросить новую CoderMalfunctionError(x);
}
если (cr.isOverflow())
вернуть кр;
если (cr.isUnderflow()) {
if (endOfInput && in.hasRemaining()) {
cr = CoderResult.malformedForLength(in.remaining());
} еще {
вернуть кр;
}
}
Действие CodingErrorAction = null;
если (cr.isMalformed())
действие = MalformedInputAction;
иначе, если (cr.isUnmappable())
действие = unmapableCharacterAction;
еще
утверждать ложь: cr.toString();
если (действие == CodingErrorAction.REPORT)
вернуть кр;
если (действие == CodingErrorAction.REPLACE) {
если (out.remaining() < замена.длина)
вернуть CoderResult.OVERFLOW;
out.put (замена);
}
if ((действие == 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("Инцзюань?".getBytes("gbk"), "utf-8").getBytes());
}
public static void printArray(byte[] bs){
for(int я = 0; я <bs.length; я++){
System.out.print(bs[i] + " ");
}
Система.out.println();
}
Вывод описан в комментариях к программе:
Поскольку 2 байта в GBK представляют китайский иероглиф, в нем 6 байт;
Поскольку 3 байта в UTF-8 представляют китайский символ, их всего 9;
Поскольку для создания строки в соответствии с правилами UTF-8 используется массив байтов, который не может быть сгенерирован GBK, отображается ???;
По этой причине часто встречаются искаженные символы. GBK использует байт, сгенерированный UTF-8, для создания строк;
Хотя сгенерированный выше код искажен, компьютер так не считает, поэтому он все равно может получить массив байтов через getBytes, и UTF-8 в этом массиве может быть распознан;
Последние два 63(?) должны быть заполнены encode (или не хватает байт для непосредственного заполнения, я не внимательно посмотрел это место);
Поскольку кодировка букв и цифр в 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 имеют одинаковую кодировку %, цифр и букв, поэтому строка после кодирования может быть на 100% гарантированной одинаковой в этих двух кодировках, а затем декодироваться для получения символов. .Просто вертел. Судя по формату String, мы можем догадаться, что эффективность кодирования и декодирования очень и очень высока, так что это тоже хорошее решение.
Второе решение: унифицированный формат кодирования <BR> Здесь мы используем майнинг Webx. Вам нужно только установить defaultCharset="UTF-8" в webx.xml.