character encoding
Смотреть что такое «character encoding» в других словарях:
Character encoding — Special characters redirects here. For the Wikipedia editor s handbook page, see Help:Special characters. A character encoding system consists of a code that pairs each character from a given repertoire with something else, such as a sequence of… … Wikipedia
character encoding — noun A well defined correspondence between characters and numbers used to represent them. See Also: character set … Wiktionary
Chinese character encoding — In computing, Chinese character encodings can be used to represent text written in the CJK languages Chinese, Japanese, Korean and (rarely) obsolete Vietnamese, all of which use Chinese characters. Several general purpose character encodings… … Wikipedia
HZ (character encoding) — The HZ character encoding is an encoding of GB2312 that was formerly commonly used in email and USENET postings. It was designed in 1989 by Fung Fung Lee (李楓峰) of Stanford University, and subsequently codified in 1995 into RFC 1843.The HZ (short… … Wikipedia
Encoding — is the process of transforming information from one format into another. The opposite operation is called decoding. There are a number of more specific meanings that apply in certain contexts:*Encoding (in cognition) is a basic perceptual process … Wikipedia
Character encodings in HTML — For a list of character entity references, see List of XML and HTML character entity references. HTML HTML and HTML5 Dynamic HTML XHTML XHTML Mobile Profile and C HTML Canvas element Character encodings Document Object Model Font family HTML… … Wikipedia
Character (computing) — In computer and machine based telecommunications terminology, a character is a unit of information that roughly corresponds to a grapheme, grapheme like unit, or symbol, such as in an alphabet or syllabary in the written form of a natural… … Wikipedia
character set — noun a) The set of characters encoded by a given character encoding. b) A set of characters together with a character encoding. See Also: charset, character encoding, DRCS … Wiktionary
Character large object — CLOB redirects here. For the card game, see Clob (card). For the formerly proposed securities trading system, see central limit order book. A Character Large Object (or CLOB) is a collection of character data in a database management system,… … Wikipedia
Percent-encoding — For the urlencode in MediaWiki, see Help:Magic words. Percent encoding, also known as URL encoding, is a mechanism for encoding information in a Uniform Resource Identifier (URI) under certain circumstances. Although it is known as URL encoding… … Wikipedia
Variable-width encoding — This article is about the storage of text in computers. For the transmission of data across noisy channels, see variable length code. A variable width encoding is a type of character encoding scheme in which codes of differing lengths are used to … Wikipedia
Юникод для чайников
Сам я не очень люблю заголовки вроде «Покемоны в собственном соку для чайников\кастрюль\сковородок», но это кажется именно тот случай — говорить будем о базовых вещах, работа с которыми довольно часто приводить к купе набитых шишек и уйме потерянного времени вокруг вопроса — «Почему же оно не работает?». Если вы до сих пор боитесь и\или не понимаете Юникода — прошу под кат.
Зачем?
Главный вопрос новичка, который встречается с впечатляющим количеством кодировок и на первый взгляд запутанными механизмами работы с ними (например, в Python 2.x). Краткий ответ — потому что так сложилось 🙂
Кодировкой, кто не знает, называют способ представления в памяти компьютера (читай — в нулях-единицах\числах) цифр, буков и всех остальных знаков. Например, пробел представляется как 0b100000 (в двоичной), 32 (в десятичной) или 0x20 (в шестнадцатеричной системе счисления).
Так вот, когда-то памяти было совсем немного и всем компьютерам было достаточно 7 бит для представления всех нужных символов (цифры, строчный\прописной латинский алфавит, куча знаков и так называемые управляемые символы — все возможные 127 номеров были кому-то отданы). Кодировка в это время была одна — ASCII. Шло время, все были счастливы, а кто не был счастлив (читай — кому не хватало знака «©» или родной буквы «щ») — использовали оставшиеся 128 знаков на свое усмотрение, то есть создавали новые кодировки. Так появились и ISO-8859-1, и наши (то есть кириличные) cp1251 и KOI8. Вместе с ними появилась и проблема интерпретации байтов типа 0b1******* (то есть символов\чисел от 128 и до 255) — например, 0b11011111 в кодировке cp1251 это наша родная «Я», в тоже время в кодировке ISO-8859-1 это греческая немецкая Eszett (подсказывает Moonrise) «ß». Ожидаемо, сетевая коммуникация и просто обмен файлами между разными компьютерами превратились в чёрт-знает-что, несмотря на то, что заголовки типа ‘Content-Encoding’ в HTTP протоколе, email-письмах и HTML-страницах немного спасали ситуацию.
В этот момент собрались светлые умы и предложили новый стандарт — Unicode. Это именно стандарт, а не кодировка — сам по себе Юникод не определяет, как символы будут сохранятся на жестком диске или передаваться по сети. Он лишь определяет связь между символом и некоторым числом, а формат, согласно с которым эти числа будут превращаться в байты, определяется Юникод-кодировками (например, UTF-8 или UTF-16). На данный момент в Юникод-стандарте есть немного более 100 тысяч символов, тогда как UTF-16 позволяет поддерживать более одного миллиона (UTF-8 — и того больше).
Ближе к делу!
Естественно, есть поддержка Юникода и в Пайтоне. Но, к сожалению, только в Python 3 все строки стали юникодом, и новичкам приходиться убиваться об ошибки типа:
Давайте разберемся, но по порядку.
Зачем кто-то использует Юникод?
Почему мой любимый html-парсер возвращает Юникод? Пусть возвращает обычную строку, а я там уже с ней разберусь! Верно? Не совсем. Хотя каждый из существующих в Юникоде символов и можно (наверное) представить в некоторой однобайтовой кодировке (ISO-8859-1, cp1251 и другие называют однобайтовыми, поскольку любой символ они кодируют ровно в один байт), но что делать если в строке должны быть символы с разных кодировок? Присваивать отдельную кодировку каждому символу? Нет, конечно, надо использовать Юникод.
Зачем нам новый тип «unicode»?
Вот мы и добрались до самого интересного. Что такое строка в Python 2.x? Это просто байты. Просто бинарные данные, которые могут быть чем-угодно. На самом деле, когда мы пишем что-нибудь вроде:интерпретатор не создает переменную, которая содержит первые четыре буквы латинского алфавита, но только последовательность с четырёх байт, и латинские буквы здесь используются исключительно для обозначения именно этого значения байта. То есть ‘a’ здесь просто синоним для написания ‘\x61’, и ни чуточку больше. Например:
И ответ на вопрос — зачем нам «unicode» уже более очевиден — нужен тип, который будет представятся символами, а не байтами.
Хорошо, я понял чем есть строка. Тогда что такое Юникод в Пайтоне?
«type unicode» — это прежде всего абстракция, которая реализует идею Юникода (набор символов и связанных с ними чисел). Объект типа «unicode» — это уже не последовательность байт, но последовательность собственно символов без какого либо представления о том, как эти символы эффективно сохранить в памяти компьютера. Если хотите — это более высокой уровень абстракции, чем байтовый строки (именно так в Python 3 называют обычные строки, которые используются в Python 2.6).
Как пользоваться Юникодом?
Как из юникод-строки получить обычную? Закодировать её:
Алгоритм кодирования естественно обратный приведенному выше.
Не кодируется 🙁
Разберем примеры с начала статьи. Как работает конкатенация строки и юникод-строки? Простая строка должна быть превращена в юникод-строку, и поскольку интерпретатор не знает кодировки, от использует кодировку по умолчанию — ascii. Если этой кодировке не удастся декодировать строку, получим некрасивую ошибку. В таком случае нам нужно самим привести строку к юникод-строке, используя правильную кодировку:
«UnicodeDecodeError» обычно есть свидетельством того, что нужно декодировать строку в юникод, используя правильную кодировку.
Теперь использование «str» и юникод-строк. Не используйте «str» и юникод строки 🙂 В «str» нет возможности указать кодировку, соответственно кодировка по умолчанию будет использоваться всегда и любые символы > 128 будут приводить к ошибке. Используйте метод «encode»:
«UnicodeEncodeError» — знак того, что нам нужно указать правильную кодировку во время превращения юникод-строки в обычную (или использовать второй параметр ‘ignore’\’replace’\’xmlcharrefreplace’ в методе «encode»).
Хочу ещё!
Хорошо, используем бабу-ягу из примера выше ещё раз:
Есть ещё способ использования «u»» для представления, например, кириллицы, и при этом не указывать кодировку или нечитабельные юникод-поинты (то есть «u’\u1234’»). Способ не совсем удобный, но интересный — использовать unicode entity codes:
Ну и вроде всё. Основные советы — не путать «encode»\«decode» и понимать различия между байтами и символами.
Python 3
Здесь без кода, ибо опыта нет. Свидетели утверждают, что там всё значительно проще и веселее. Кто возьмется на кошках продемонстрировать различия между здесь (Python 2.x) и там (Python 3.x) — респект и уважуха.
Полезно
Раз уж мы о кодировках, порекомендую ресурс, который время-от-времени помогает побороть кракозябры — http://2cyr.com/decode/?lang=ru.
Unicode HOWTO — официальный документ о том где, как и зачем Юникод в Python 2.x.
Спасибо за внимание. Буду благодарен за замечания в приват.
Термин символ используется здесь в общем смысле того, что читатель воспринимает как отдельный отображаемый элемент. Распространенными примерами являются буква «а», символ «@» и эмодзи «🐂». Иногда то, что выглядит как один символ, на самом деле состоит из нескольких независимых отображаемых элементов, как описано в разделе о кластерах графем.
Типы string и char
Следующий пример функции выводит значения в шестнадцатеричной нотации всех экземпляров char в string :
Передайте string «Hello» в эту функцию, и вы получите следующие выходные данные:
Кодовые точки Юникода
Юникод — это международный стандарт кодирования, используемый на различных платформах и с различными языками и скриптами.
Стандарт Юникода определяет более 1,1 миллиона кодовых точек. Кодовая точка — это целочисленное значение, которое может быть в диапазоне от 0 до U+10FFFF (десятичное число 1 114 111). Некоторые кодовые точки назначаются буквам, символам или эмодзи. Другие назначаются действиям, которые определяют способ отображения текста или символов, например переход на новую строку. Многие кодовые точки еще не назначены.
Вот несколько примеров назначения кодовых точек со ссылками на диаграммы Юникода, в которых они появляются:
| Decimal | Hex | Пример | Описание |
|---|---|---|---|
| 10 | U+000A | Н/Д | Перевод строки |
| 97 | U+0061 | а | Латинская строчная буква A |
| 562 | U+0232 | Ȳ | Латинская заглавная буква Y со знаком долготы |
| 68 675 | U+10C43 | 𐱃 | Древнетюркская буква (язык орхоно-енисейских надписей) |
| 127 801 | U+1F339 | 🌹 | Эмодзи «Роза» |
В пределах всего диапазона кодовых точек существует два поддиапазона:
На следующей схеме показана взаимосвязь между BMP и дополнительными кодовыми точками.
Единицы кода UTF-16
Суррогатные пары
На следующей схеме показана взаимосвязь между BMP и суррогатными кодовыми точками.
Когда за старшей заменяющей кодовой точкой ( U+D800..U+DBFF ) сразу же следует младшая заменяющая кодовая точка ( U+DC00..U+DFFF ), пара интерпретируется как дополнительная кодовая точка с помощью следующей формулы:
Вот та же формула, но с использованием десятичной нотации:
Старшая заменяющая кодовая точка не имеет значения числа выше, чем младшая заменяющая кодовая точка. Старшая заменяющая кодовая точка называется «старшей», потому что она используется для вычисления 11 разрядов высшего порядка полного 21-разрядного диапазона кодовых точек. Младшая заменяющая кодовая точка используется для вычисления 10 разрядов низшего порядка.
Вот тот же расчет, но с использованием десятичной нотации:
Скалярные значения Юникода
Термин скалярное значение Юникода относится ко всем кодовым точкам, кроме суррогатных. Другими словами, скалярное значение — это любая кодовая точка, которой присвоен символ или которой может быть присвоен символ в будущем. Слово «символ» здесь относится ко всему, что может быть назначено кодовой точке, включая действия, которые определяют способ отображения текста или символов.
На приведенной ниже схеме показаны точки кода скалярного значения.
Тип Rune как скалярное значение
В следующем примере создается исключение, так как кодовая точка находится в суррогатном диапазоне и не является частью суррогатной пары:
В следующем примере создается исключение, так как кодовая точка находится за пределами дополнительного диапазона:
Пример использования Rune: изменение регистра букв
API, который принимает char и предполагает, что работает с кодовой точкой, которая является скалярным значением, работает неправильно, если char принадлежит суррогатной паре. Например, рассмотрим следующий метод, который вызывает Char.ToUpperInvariant для каждого экземпляра char в string:
Вот два варианта правильного преобразования string в верхний регистр:
Другие API-интерфейсы Rune
Кластеры графем
Рассмотрим экземпляры string «a», «á», «á» и » 👩🏽🚒 «. Если операционная система обрабатывает их в соответствии со стандартом Юникода, каждый из этих экземпляров string отображается в виде одного текстового элемента или кластера графем. Однако последние два представлены более чем одной кодовой точкой скалярного значения.
В некоторых из приведенных выше примерах, таких как комбинированный модификатор диакритических знаков или модификатор тона кожи, кодовая точка не отображается как отдельный элемент на экране. Вместо этого она служит для изменения внешнего вида текстового элемента, который был до него. В этих примерах показано, что может потребоваться несколько скалярных значений, чтобы составить один «символ» или «кластер графем».
Пример: количество char, Rune и экземпляров текстовых элементов
Пример: разделение экземпляров string
При разделении экземпляров string не разделяйте суррогатные пары и кластеры графем. Рассмотрим приведенный ниже пример неправильного кода, который будет вставлять разрывы строк через каждые 10 символов в string.
Лучшим подходом является разделение string путем подсчета кластеров графем или текстовых элементов, как показано в приведенном ниже примере.
UTF-8 и UTF-32
Как и в системе UTF-16, для UTF-8 требуется несколько единиц кода, чтобы предоставить некоторые скалярные значения Юникода. UTF-32 может представлять любое скалярное значение в одной 32-разрядной единице кода.
Ниже приведено несколько примеров, показывающих, как одна и та же кодовая точка Юникода представлена в каждой из этих трех систем кодирования Юникода.
Как упоминалось ранее, одна единица кода UTF-16 из суррогатной пары сама по себе не имеет смысла. Таким же образом одна единица кода UTF-8 сама по себе не имеет смысла, если она находится в последовательности из двух, трех или четырех единиц, используемых для вычисления скалярных значений.
Порядок байтов
Кодирование с правильным форматом
Кодировка Юникод с правильным форматом — это экземпляр string кодовых единиц, который может быть однозначно и без ошибок декодирован в последовательность скалярных значений Юникода. Данные с правильным форматом могут быть свободно перекодированы между UTF-8, UTF-16 и UTF-32.
Вопрос в том, имеет ли последовательность кодирования правильный формат или нет, независимо от порядка байтов в архитектуре компьютера. Неверно сформированная последовательность UTF-8 имеет неправильный формат как на компьютерах с обратным порядком байтов, так и на компьютерах с прямым порядком байтов.
Вот несколько примеров кодировок с неправильным форматом:
В UTF-32 последовательность [ 0011ABCD ] имеет неправильный формат, так как 0011ABCD находится вне диапазона скалярных значений Юникода.
Литерал с неправильным форматом:
Подстрока, которая разделяет суррогатную пару:
Встроенные классы Encoding также можно настроить для создания исключения, а не для замены символов, когда отображаются последовательности с неправильным форматом. Этот подход часто используется в приложениях, требующих особых мер безопасности, где замена символов может быть неприемлемой.
Введение
Ресурсы
Нижеприведенные ссылки как минимум полезны настолько же, насколько и данная статья, а может — и более полезны. Я сам использовал их при написании данной статьи. В них много полезных и качественных материалов, и если в этой статье вы заметите какие-нибудь неточности, то данные ресурсы должны быть более точными.
Двоичные и текстовые данные – это две разные вещи
Большинство современных языков программирования (как и некоторые старые) проводят ясную черту между двоичным (бинарным) контентом и символьным (или текстовым) контентом. Хотя эта разница понимается на инстинктивном уровне, я всё-таки внесу определение.
Бинарные (двоичные) данные являются последовательностью октетов (октет состоит из 8 битов) без всякого придаваемого им естественного значения, или интерпретации. И даже если существует внешнее «толкование» того или иного набора октетов как, скажем, исполняемый файл или графическое изображение, данные сами по себе являются просто набором октетов. Далее вместо термина «октет» я буду использовать «байт», хотя, если говорить точно, не всякий байт является октетом. К примеру, существовали компьютерные архитектуры с 9-битовыми байтами. Впрочем, в данном контексте такие детали не очень-то и нужны, так что далее под термином «байт» я буду подразумевать именно 8-битовый байт.
Символьные (текстовые) данные являются последовательностью символов.
Так для чего же Юникод?
Не волнуйтесь, если всё вышесказанное выглядит странно. О различиях, описанных выше, следует знать, но на самом деле они не часто выходят на первый план. Большинство ваших задач, скорее всего, будет «крутиться» вокруг конвертации некоего набора байтов в некий текст и наоборот. В таких ситуациях вы будете работать со структурой System.Char (в C# известна под псевдонимом char ), классом System.String ( string в C#), а также с классом System.Text.Encoding.
Структура Char является самым базовым типом символа в C#, один экземпляр Char представляет один символ Юникода и занимает 2 байта памяти, а значит, может принимать любое значение из диапазона 0-65535. Имейте в виду, что не все числа из этого диапазона являются валидными символами Юникода.
Встроенные схемы кодировок
ASCII
UTF-16 и UCS-2
UTF-7, судя по моему опыту, редко когда используется, но он позволяет перекодировать Юникод (вероятно, только первые 65535 символов) в ASCII-символы (не байты!). Это может пригодиться при работе с электронной почтой в ситуациях, когда почтовые шлюзы поддерживают только символы ASCII, или даже только подмножество ASCII (к примеру, кодировку EBCDIC). Моё описание выглядит невнятно, потому что я никогда не залазил в детали UTF-7 и не собираюсь этого делать впредь. Если вам необходимо использовать UTF-7, то вы, вероятно, и так знаете достаточно, а если у вас нет абсолютной необходимости использовать UTF-7, то советую не делать этого. Экземпляр класса для кодирования в UTF-7 может быть получен при помощи свойства Encoding.UTF7.
Кодовые страницы Windows/ANSI
Windows Code Pages (кодовые страницы Windows) являются, как правило, одно- или двухбайтными наборами символов, кодирующими до 256 или 65 536 символов соответственно. Каждая кодовая страница имеет свой номер, и кодировщик для кодовой страницы с известным номером можно получить при помощи статического метода Encoding.GetEncoding(Int32). В большинстве случаев кодовые страницы полезны для работы со старыми данными, которые часто хранятся в «кодовой странице по умолчанию» (default code page). Энкодер для кодовой страницы по умолчанию может быть получен при помощи свойства Encoding.Default. Снова таки, избегайте использования кодовых страниц, когда это возможно. За дополнительной информацией обращайтесь к MSDN.
ISO-8859-1 (Latin-1)
Потоки, ридеры и райтеры
Потоки бинарны по своей природе, они читают и записывают байты. Всё, что принимает строку, должно её определённым образом преобразовать в байты, и это преобразование может быть как успешным для вас, так и нет. Эквивалентами потоков для чтения и записи текста служат абстрактные классы System.IO.TextReader и System.IO.TextWriter соответственно. Если у вас уже есть готовый поток, вы можете использовать классы System.IO.StreamReader (который непосредственно наследует TextReader ) для чтения и System.IO.StreamWriter (который непосредственно наследует TextWriter ) для записи, передавая поток в конструктор этих классов и кодируя так, как вам нужно. Если вы явно не укажете кодировку, по умолчанию будет применена UTF-8. Ниже представлен пример кода, конвертирующего файл с UTF-8 в UCS-2:
Сложные моменты
Ладно, это были лишь основы Юникода. Есть множество других нюансов, на некоторые из которых я уже намекнул, и я считаю, что людям следует о них знать, даже если они полагают, что подобное никогда с ними не произойдёт. Я не предлагаю каких-либо общих методологий или руководящих принципов — я просто пытаюсь поднять вашу осведомленность в возможных проблемах. Ниже приведён список, и он ни скольким образом не исчерпывающий. Важно, чтобы вы поняли, что большинство описанных проблем и трудностей ни в коей мере не являются виной или ошибками Консорциума Юникода; так же, как и в случае даты, времени и любой из проблем интернационализации, это — «заслуга» человечества, которое с течением времени само создало многие принципиально сложные проблемы.
Культуро-зависимый поиск, сортировка и проч.
Суррогатные пары
Модифицирующие символы
Не все символы из Юникода в результате вывода на экран или бумагу предстают в виде значка/картинки. Подчёркнутый (акцентированный) символ может быть представлен в качестве двух других символов: обычного, неподчёркнутого символа и следующего за ним символа подчёркивания, который называется модифицирующим (или комбинируемым) символом (Combining character). Некоторые графические интерфейсы поддерживают модифицирующие символы, некоторые нет, и работа вашего приложения будет зависеть от того, какое предположение вы сделаете.
Нормализация
Частично из-за таких вещей, как модифицирующие символы, может быть несколько способов представления того, что в некотором смысле является одним символом. Литера «á» с ударением, к примеру, может быть представлена одним символом «а» без ударения и следующим за ним модифицирующим символом ударения, а может быть представлена только одним символом, представляющим готовую литеру «а» с ударением. Последовательности символов могут быть нормализованы таким образом, чтобы использовать модифицирующие символы везде, где это возможно, или же наоборот — не использовать их везде, где их можно заменить одиночным символом. Должно ли ваше приложение рассматривать две строки, содержащие литеру «á» с ударением, но в одной представленную двумя символами, а во второй одним, как равные, или как разные? А что насчёт сортировки? Производят ли сторонние компоненты и библиотеки, используемые вами, нормализацию строк, и вообще, учитывают ли подобные нюансы? На эти вопросы отвечать вам.
Отладка проблем с Юникодом
Этот раздел (в оригинале это отдельная статья – прим. пер.) описывает, что делать в очень специфических ситуациях. А именно, у вас есть некоторые символьные данные (попросту — текст) в одном месте (как правило, в базе данных), которые проходят через разные шаги/слои/компоненты и затем выводятся пользователю (как правило, на веб-странице). И, к сожалению для вас, некоторые символы отображены неверно (крякозябры). Исходя из множества шагов-этапов, по которым проходят ваши текстовые данные, проблема может возникать во многих местах. Это страница поможет вам просто и надёжно узнать, что и где «сломалось».
Шаг 1: поймите основы Юникода
А попросту говоря — прочтите основной текст статьи. Можете также обратить внимание на ссылки, которые приведены в начале статьи. Факт в том, что без базовых знаний вам будет туговато.
Шаг 2: попытайтесь определить, какие конвертации могли произойти
Если вы можете понять, где, возможно, всё ломается, то этот участок/этап намного проще будет изолировать. Вместе с тем имейте в виду, что проблема может быть не в процессе извлечения и преобразования текста из хранилища, а в том, что уже «испорченный» текст был занесён в хранилище ранее. (С подобными проблемами я стыкался в прошлом, когда, к примеру, одно старое приложение искажало текст при записи и считывании его в/из БД. «Прикол» был в том, что ошибки конвертации накладывались друг на друга и взаимокомпенсировались, так что на выходе получался корректный текст. Вообще приложение работало нормально, но стоило его тронуть — и всё рассыпалось.) К действиям, которые могут «портить» текст, следует относить выборку из БД, чтение из файла, передачу через веб-соединение и выведение текста на экран.
Шаг 3: проверяйте данные на каждом этапе
Первое правило: не доверяйте ничему, что логирует символьные данные в виде последовательности глифов (т.е. стандартных значков символов). Вместо этого вы должны логировать данные как набор кодов символов в виде байтов. Например, если я имею строку, содержащую слово «hello», то я её отображу как «0068 0065 006C 006C 006F». (Использование шестнадцатеричных кодов позволит вам легко проверить символ по кодовым таблицам.) Чтобы это сделать, надо пройтись по всем символам в строке и для каждого символа вывести его код, что и делается в нижеприведённом методе, который отображает результат в консоль:
Ваш собственный метод логирования будет иным в зависимости от вашего окружения, но основа его должна быть именно такой, как я привёл. Более продвинутый способ отладки и логирования символьных данных я привёл в своей статье, посвящённой строкам.
Суть моей идеи в том, чтобы избавиться от всевозможных проблем с кодировками, шрифтами и т.д. Эта методика может быть полезна при работе со специфичными символами Юникода. Если же вы не можете корректно логировать шестнадцатеричные коды даже простого ASCII-текста — у вас большие проблемы.
Следующий этап — убедитесь, что у вас есть тест-кейс, который можно использовать. Найдите желательно небольшой набор исходных данных, на котором ваше приложение гарантированно «сбоит», удостоверьтесь, что вы точно знаете, каким должен быть правильный результат, и залогируйте получившийся результат во всех проблемных местах.
После того, как проблемная строка залогирована, надо удостовериться, является ли она такой, какой должна быть, или нет. В этом вам поможет веб-страница Unicode code charts. Вы можете выбрать как набор символов, в котором уверены, так и искать символы в алфавитном порядке. Убедитесь, что каждый символ в строке имеет правильное значение. Как только вы найдёте то место в вашем приложении, где поток символьных данных поврежден, исследуйте это место, выясните причину ошибки и исправьте её. Исправив все ошибки, убедитесь, что приложение работает корректно.
Заключение
Как и в случае с подавляющим большинством ошибок, возникающих в разработке ПО, проблемы с текстом решаются при помощи универсальной стратегии «разделяй и властвуй». Как только вы будете уверены в каждом шаге, вы сможете быть уверенными в целом. Если во время исправления подобных ошибок вы столкнётесь с особенно непонятными и странными их проявлениями, я настоятельно советую после их исправления покрыть данный участок кода юнит-тестами; они будут служить и документацией, показывающей, что может случиться, и защитой от будущих регрессий.



