1. Предисловие
Фактически, с тех пор, как я начал писать код Java, я столкнулся с бесчисленными проблемами искаженного и транскодирования, таких как искаженное код, который встречается при чтении из текстового файла в строку, искаженного кода, который возникает при получении параметров HTTP -запроса в сервисе, загарный код, который возникает при запросе JDBC и т. Д. Эти проблемы очень распространены. Когда вы столкнетесь с ними, вы можете успешно решить их, ища их, чтобы у вас не было глубокого понимания.
До двух дней назад мой одноклассник говорил со мной о проблеме кодирования исходного файла Java (эта проблема анализируется в последнем примере) и началась с этой проблемы и началась с ряда проблем. Затем мы обсуждали во время поиска информации. Было поздно ночью, когда мы наконец нашли ключевую подсказку в блоге, решая все сомнения, и предложения, которые мы раньше не поняли, могли быть четко объяснены. Поэтому я решил использовать это эссе, чтобы записать мое понимание некоторых проблем кодирования и результатов эксперимента.
Некоторые из следующих концепций являются моим собственным пониманием, основанным на фактических условиях. Если есть какие -либо ошибки, обязательно поправьте их.
2. Концепция резюме
В первые дни Интернет еще не разработал, и компьютеры использовались только для обработки некоторых местных данных, так много стран и регионов разработали схемы кодирования для местных языков. Этот вид регионального кодирования коллективно называется кодированием ANSI (потому что они являются расширениями до кодов ANSI-ASCII). Тем не менее, они заранее не обсуждали, как быть совместимыми друг с другом, а вместо этого делали свой собственный, что закладывало корень кодирования конфликтов. Например, кодирование GB2312, используемое в материковом конфликте с кодированием BIG5, использованным на Тайване. Те же два байта представляют разные символы в двух схемах кодирования. С ростом интернета документ часто содержит несколько языков, и компьютер сталкивается с проблемами при его отображении, потому что он не знает, каким кодированию этих двух байтов принадлежат.
Такие проблемы распространены в мире, поэтому призывы к переосмыслению общего набора персонажей и объединенной нумерации всех персонажей в мире растут.
В результате появился код Unicode, он равномерно пронумеровал всех персонажей в мире. Поскольку он может уникально идентифицировать символ, шрифт должен быть разработан только для кода Unicode. Тем не менее, стандарт Unicode определяет набор символов, но не указывает схему кодирования, то есть он только определяет абстрактные числа и соответствующие символы, но не указывает, как сохранить строку чисел Unicode. Реальным требованием является то, как хранить UTF-8, UTF-16, UTF-32 и другие решения. Следовательно, кодировки с UTF Beginnings могут быть непосредственно преобразованы с помощью расчетов и значений Unicode (CodePoints, кодовые точки). Как следует из названия, UTF-8 представляет собой 8-битное кодирование длины, которая представляет собой кодирование с переменной длиной, используя от 1 до 6 байтов для кодирования символа (поскольку он ограничен диапазоном Unicode, на самом деле это всего лишь 4 байта максимума); UTF-16-это 16-битное базовое кодирование блока, которое также представляет собой кодирование переменной длины, либо 2 байта, либо 4 байта; UTF-32 является фиксированной длиной, а фиксированные 4 байта хранят номер Unicode.
На самом деле, я всегда был немного недоразумением в отношении Unicode. В моем впечатлении код Unicode может достичь только 0xffff, что означает, что он может представлять только 2^16 символов. После тщательного чтения Википедии я понял, что ранняя схема кодирования UCS-2 действительно была такой. UCS-2 пристально использовал два байта для кодирования символа, поэтому он может кодировать только символы в диапазоне BMP (основная многоязычная плоскость, то есть 0x0000-0xffff, который содержит наиболее часто используемые символы в мире). Чтобы кодировать символы с Unicode, превышающим 0xffff, люди расширили кодирование UCS-2 и создали кодирование UTF-16, которое является переменной длиной. В диапазоне BMP UTF-16 точно такой же, как UCS-2, в то время как UTF-16 за пределами BMP использует 4 байта для хранения.
Чтобы облегчить описание ниже, позвольте мне объяснить концепцию кодового блока (CodeUnit). Основной компонент определенного кодирования называется кодовым блоком. Например, кодовая единица UTF-8 составляет 1 байт, а кодовая единица UTF-16 составляет 2 байта. Это трудно объяснить, но это легко понять.
Чтобы быть совместимым с различными языками и лучшим кроссплатформенным, Javastring сохраняет код Unicode для символов. Он использовал для использования схемы кодирования UCS-2 для хранения Unicode. Позже он обнаружил, что символов в диапазоне BMP было недостаточно, но для соображений потребления памяти и совместимости они не поднимались до UCS-4 (то есть UTF-32, фиксированное 4-байтовое кодирование), но приняли указанный выше UTF-16. Тип ChAR можно рассматривать как его кодовый блок. Эта практика вызывает некоторые проблемы. Если все символы находятся в диапазоне BMP, это нормально. Если есть символы за пределами BMP, это больше не кодовое устройство, соответствующее символу. Метод длины возвращает количество кодовых единиц, а не количество символов. Метод Charat, естественно, возвращает кодовую единицу вместо символа, который становится неприятным при переселении. Хотя некоторые новые методы работы предоставляются, это все еще неудобно и не может быть получено случайным образом.
Кроме того, я обнаружил, что Java не обрабатывает литералы Unicode, превышающие 0xFFFF при компиляции, поэтому, если вы не можете ввести не BMP-символ, но вы знаете его код Unicode, вам нужно использовать относительно глупый метод, чтобы позволить строке хранить его: вручную вычислить кодирование UTF-16 (четыре байта) персонажа и использование первых двух по два итоги, а затем. Пример кода заключается в следующем.
public static void main (string [] args) {// string str = ""; // Мы хотим назначить такой символ, предполагая, что мой метод ввода не может быть набран //, но я знаю, что его Unicode - 0x1d11e // string str = "/u1d11e"; // это не будет распознано //, поэтому его можно рассчитать через UTF-16, кодирующую D834 DD1ESTRING Str = "/ud834/udd1e"; // затем написать System.out.println (str); // Успешный вывод ""}Блокнот, который поставляется с окнами, может быть сохранен как кодирование Unicode, что фактически относится к кодированию UTF-16. Как упомянуто выше, используемые основные кодирования персонажа находятся в диапазоне BMP, а в диапазоне BMP значение кодирования UTF-16 каждого символа равна соответствующему значению Unicode, что, вероятно, поэтому Microsoft называет его Unicode. Например, я ввел два символа «Good A» в блокноте, а затем сохранил его как кодирование Big Endian (High Bit Bit), и открыл файл с WinHex. Содержание, как показано на рисунке ниже. Первые два байта файла называются байтовыми заказами (байт -отметка), (Fe FF) отмечает эндский заказ как приоритет высокого бита, а затем (59 7d) является «хорошим» кодом Unicode, а (00 61) - код «Unicode Unicode».
При коде Unicode проблема не может быть решена немедленно, потому что в первую очередь существует большое количество стандартных данных, не являющихся Unicode, в мире, и мы не можем их отказаться. Во -вторых, кодирование Unicode часто занимает больше места, чем кодирование ANSI, поэтому с точки зрения сохранения ресурсов, кодирование ANSI все еще необходимо. Следовательно, необходимо установить механизм преобразования, чтобы кодирование ANSI мог быть преобразовано в Unicode для единой обработки, или Unicode может быть преобразован в кодирование ANSI для удовлетворения требований платформы.
Метод преобразования относительно легко сказать. Для серии UTF или ISO-8859-1 совместимые кодирования могут быть непосредственно преобразованы с помощью расчетов и значений Unicode (на самом деле, это также может быть поиск таблицы). Для кодирования ANSI, оставленного от системы, это можно сделать, только посмотрев на стол. Microsoft вызывает эту коду сопоставления таблицы сопоставления (кодовая страница) и классифицирует и пронумерованную по кодированию. Например, наша общая страница CP936-это кодовая страница GBK, а CP65001-кодовая страница UTF-8. Следующим рисунком является таблица картирования GBK-> Unicode, найденная на официальном веб-сайте Microsoft (визуально неполный). Точно так же должна быть обратная таблица картирования Unicode-> GBK.
С помощью кодовой страницы вы можете легко выполнить различные преобразования кодирования. Например, преобразование из GBK в UTF-8, вам нужно разделить только данные на символы в соответствии с правилами кодирования GBK, используйте кодированные данные каждого символа, чтобы проверить страницу кода GBK, получить его значение Unicode, а затем использовать Unicode для проверки кодовой страницы UTF-8 (или непосредственно рассчитывать), и вы можете получить соответствующий приклад UNICODE. То же самое касается наоборот. Примечание. UTF-8 является стандартной реализацией Unicode. Его кодовая страница содержит все значения Unicode, поэтому любая кодировка конвертируется в UTF-8, а затем преобразованный обратно не будет потерян. На этом этапе мы можем сделать вывод о том, что для завершения работы по конверсии кодирования наиболее важным является успешно преобразовать в Unicode, так что правильно выбрать набор символов (кодовая страница) является ключом.
После понимания характера проблемы с потерей транскодирования я внезапно понял, почему структура JSP использовала ISO-8859-1 для декодирования параметров запроса HTTP, что привело к тому, что нам пришлось написать такие утверждения, когда мы получили китайские параметры:
Stringparam=newString(s.getBytes("iso-8859-1"),"UTF-8");
Поскольку фреймворк JSP получает бинарный байтовый поток, кодированный параметром, он не знает, что это за кодирование (или не заботится), и не знает, какую страницу кода следует проверить для конвертации в Unicode. Затем он выбрал решение, которое никогда не вызовет потери. Предполагается, что это данные, кодируемые ISO-8859-1, а затем ищет кодовую страницу ISO-8859-1, чтобы получить последовательность Unicode. Поскольку ISO-8859-1 кодируется байтами, и в отличие от ASCII, он кодирует каждый кусочек пространства 0 ~ 255, поэтому на его кодовой странице можно найти любой байт. Если он будет переведен от Unicode в исходный байтовый поток, потери не будут. Таким образом, для европейских и американских программистов, которые не рассматривают другие языки, они могут непосредственно расшифровать строку в рамках JSP. Если они хотят быть совместимыми с другими языками, им нужно вернуться только к исходному байтовому потоку и декодировать его на фактической кодовой странице.
Я закончил объяснять связанные концепции Unicode и кодировки символов. Далее я буду использовать примеры Java, чтобы испытать это.
Iii. Пример анализа
1. Преобразовать в конструктор по стрессу юникода
Метод конструкции строки заключается в преобразовании различных кодируемых данных в последовательность Unicode (хранясь в кодировании UTF-16). Следующий тестовый код используется, чтобы показать применение метода строительства Javastring. Примеры не BMP участвуют, поэтому методы CodePointat не используются.
Общественный тест класса {public static void main (string [] args) бросает ioException {// "hello", кодируемая GBK Byte [] gbkdata = {(byte) 0xc4, (byte) 0xe3, (byte) 0xba, (byte) 0xc3}; // "hello". {(byte) 0xa7, (byte) 0x41, (byte) 0xa6, (byte) 0x6e}; // Создать строку и декодировать ее в одноизодирующую strfromgbk = new String (gbkdata, "gbk"); String strbromgig5 = new String (big5data, "big5") ShowUnicode (strpromgbk); ShowUnicode (strfrombig5);} public static void showunicode (string str) {for (int i = 0; i <str.length (); i ++) {System.out.printf ("// u%x", (int) str.charat (i));} system.out.println ();}}}}}}}Результаты работы следующие
Можно обнаружить, что, поскольку строки Masters Unicode Code должны быть преобразованы в другие кодировки Soeasy!
3. Использование Unicode в качестве моста для реализации кодирующего взаимного преобразования
Благодаря основанию двух вышеупомянутых частей очень просто реализовать кодирование и взаимное преобразование. Вам просто нужно использовать их вместе. Во -первых, Newstring преобразует исходные кодируемые данные в последовательность Unicode, а затем вызывает Getbytes для передачи в указанное кодирование.
Например, очень простой код преобразования GBK в BIG5 заключается в следующем
public static void main (string [] args) бросает UnsupportedEncodingException {// Предположим, что это данные, считываемая из файла в байтовом потоке (кодирование GBK) Byte [] gbkdata = {(byte) 0xc4, (byte) 0xe3, (byte) 0xba, (byte) 0xc3}; String (gbkdata, "gbk"); // преобразовать из Unicode в Big5 кодирование байта [] big5data = tmp.getbytes ("big5"); // вторая операции ...}4. Проблема с потерей кодирования
Как объяснено выше, причина, по которой фреймворк JSP использует набор символов ISO-8859-1, чтобы расшифровать его. Сначала используйте пример для моделирования этого процесса восстановления, код следующим образом
Общедоступный тест класса {public static void main (string [] args) бросает UnsupportedEncodingException {// jsp framework получает 6 байтов данных Byte [] data = {(byte) 0xe4, (byte) 0xbd, (byte) 0xa0, (byte) 0xe5, (byte) 0xa5, (byte) 0xbd; ShowBytes (data); // jsp framework предполагает, что он представляет собой кодирование ISO-8859-1, генерирует строковую строку TMP = новую строку (Data, "iso-8859-1"); // ********************* Результат декодирования: « + tmp); // так сначала получите исходные 6 байтов данных (обратно поищите кодовой страницы ISO-8859-1) Byte [] utfdata = tmp.getbytes (« iso-8859-1 »); // Печать восстановленные данные (UTFDATA); UTF-8 для восстановления строки строки resul = resul = new String (utfdata, "utf-8"); // Печать снова, это правильно! System.out.println ("Результат декодирования UTF-8:" + result);} public static void showbytes (byte [] data) {for (byte b: data) system.out.printf ("0x%x", b); system.out.println ();}}Результат бега выглядит следующим образом. Первый выход неверен, потому что правила декодирования неверны. Я также неправильно проверил кодовую страницу и получил неправильный Unicode. Затем я обнаружил, что данные могут быть идеально восстановлены через неверную проверку обратной связи Unicode кодовой страницы ISO-8859-1.
Это не главное. Если ключ состоит в том, чтобы заменить «Китай» на «Китай», компиляция будет успешной, а результат работы показан на рисунке ниже. Кроме того, можно также обнаружить, что, когда количество китайских иероглифов нечетное, компиляция не удается и когда число ровно, оно проходит. Почему это? Давайте подробно проанализируем это ниже.
Поскольку Javastring использует Unicode внутренне, компилятор будет транскодом наших строковых литералов во время компиляции и конвертируется из кодирования исходного файла в Unicode (Википедия говорит, что использует немного другое кодирование от UTF-8). При компиляции мы не указали параметр кодирования, поэтому компилятор по умолчанию декодирует его в GBK. Если у вас есть некоторые знания о UTF-8 и GBK, вы должны знать, что, как правило, китайскому персонау нужно 3 байта, чтобы использовать кодирование UTF-8, в то время как GBK требуется всего 2 байта. Это может объяснить, почему паритет числа символов повлияет на результат, потому что, если есть 2 символа, кодирование UTF-8 занимает 6 байт, а декодирование в GBK может быть декодировано до 3 символов. Если это 1 символ, будет непоколебимый байт, который является тем местом, где знак вопроса на рисунке.
Чтобы быть более конкретным, кодирование UTF-8 слова «Китай» в исходном файле-E4B8ADE59BBD. Компилятор декодирует его в GBK. Пары 3 байтов ищут CP936, чтобы получить 3 значения Unicode, которые составляют 6D93E15E6D57 соответственно, соответственно, соответственно, соответственно, соответственно, что три странных символа на графике результатов. Как показано на рисунке ниже, после компиляции, эти три единокода фактически хранятся в кодировании UTF-8 в файле .class. При запуске Unicode хранится в JVM. Однако, когда окончательный вывод выводит, он все равно будет кодироваться и передаваться на терминал. Согласованное кодирование на этот раз - это кодирование, установленное области системы, поэтому, если настройки кодирования терминала будут изменены, оно все равно будет искажено. Наш E15E здесь не определяет соответствующие символы в стандарте Unicode, поэтому дисплей будет отличаться под разными шрифтами на разных платформах.
Можно представить, что если исходный файл хранится в кодировании GBK, а затем заставляет компилятора, говоря, что он является UTF-8, его в основном нельзя собрать и передавать независимо от того, сколько китайских иероглифы вводится, потому что кодирование UTF-8 очень регулярно, а случайно комбинированные байты не будут соблюдать правила UTF-8.
Конечно, самый прямой способ позволить компилятору правильно преобразовать кодирование в Unicode, - это честно сказать компилятору, каково это кодирование исходного файла.
4. Резюме
После этого сбора и эксперимента я узнал много концепций, связанных с кодированием, и познакомился с конкретным процессом преобразования кодирования. Эти идеи могут быть обобщены на различные языки программирования, а принципы реализации аналогичны. Поэтому я думаю, что в будущем больше не буду не знать о такой проблеме.
Выше приведено все содержание этой статьи о примерах концепции кодирования, таких как ANSI, Unicode, BMP, UTF и т. Д. Я надеюсь, что это будет полезно для всех. Заинтересованные друзья могут продолжать ссылаться на другие связанные темы на этом сайте. Если есть какие -либо недостатки, пожалуйста, оставьте сообщение, чтобы указать это. Спасибо, друзья, за вашу поддержку на этом сайте!