Представьте себе сценарий, в котором Боб хочет отправить сообщение Алисе, но их компьютеры используют разные методы для представления букв. Компьютер Боба использует цифру 1 для обозначения буквы «А», а компьютер Алисы — цифру 2. Это несоответствие означает, что если Боб отправит Алисе сообщение «Привет, Алиса!», Компьютер Алисы не сможет его понять. Решение этой проблемы заключается в использовании стандарта кодировки символов. Стандарт кодировки символов — это способ представления букв, цифр и других символов в виде чисел. В этой статье будет объяснено кодирование символов с точки зрения трех основных понятий: ASCII, Unicode и UTF-8.

ASCII и кодовые точки

ASCII (Американский стандартный код обмена информацией) — один из самых ранних стандартов кодировки символов в истории вычислений. Разработанный в 1960-х годах, он использует 7 бит для представления символов, что дает 128 возможных комбинаций. Эти 128 кодовых точек соответствуют различным символам, включая буквы, цифры, знаки препинания и управляющие символы. Однако только 95 из этих кодовых точек представляют собой печатные символы, что ограничивает область применения ASCII. Основная идея ASCII заключалась в том, чтобы обеспечить общую основу для кодирования символов в ранних компьютерных системах.

В кодировке символов кодовая точка — это числовое значение, присвоенное определенному символу. В ASCII каждый символ соответствует определенной кодовой точке. Например, «H» представлен кодовой точкой 72, «E» — кодовой точкой 69 и так далее. Ограниченного набора символов ASCII было достаточно для первых компьютеров, но не для различных мировых систем письма.

Расцвет Юникода

По мере глобального распространения информационных технологий возникла необходимость в более комплексном стандарте кодирования символов. Это привело к разработке Unicode, стандарта кодирования текста, поддерживаемого Консорциумом Unicode. Юникод определяет большое количество кодовых точек, официально обозначаемых шестнадцатеричными числами, начинающимися с «U+». Например, кодовая точка Юникода для буквы «H» записывается как «U+0048». Юникод может представлять символы, сценарии и символы на самых разных языках, что делает его глобальным стандартом кодировки символов.

Кодовые страницы

Кодовая страница — это кодировка символов, которая связывает набор печатных символов и управляющих символов с уникальными числами. Каждое число обычно представляет собой двоичное значение в одном байте. Хотя ASCII и Unicode являются хорошо известными стандартами кодировки символов, кодовые страницы выступают в качестве посредников, устраняющих разрыв между этими стандартами и более старыми системами.

Кодовые страницы обеспечивают определенное сопоставление символов с числовыми значениями, что позволяет компьютерам правильно интерпретировать и отображать текст в пределах схем кодирования. По сути, кодовые страницы действуют как переводчики, гарантируя правильное отображение символов в системах, которые не полностью поддерживают Unicode.

Вот несколько часто используемых кодовых страниц:

Кодовая страница 1252 (Windows-1252):Эта кодовая страница, также известная как «Windows-1252» или «западноевропейская», широко используется для кодирования символов в дополнении Latin-1 и Latin-1. расширенные наборы символов A. Он включает символы, используемые во многих западноевропейских языках, таких как английский, французский, испанский и немецкий.

Кодовая страница 1250 (Windows-1250): Эта кодовая страница, известная как «Windows-1250» или «Центральноевропейская», используется для кодирования символов центральноевропейских и восточноевропейских языков, включая польский, чешский, венгерский и хорватский.

Кодовая страница 1254 (Windows-1254): «Windows-1254», также называемая «турецкой», используется для кодирования символов турецкого языка.

UTF-8: универсальное решение

Было предпринято несколько попыток решить проблему совместимости с Unicode, например UCS2 и UTF-16. Однако наиболее широко распространенным решением в последние годы является UTF-8, что означает 8-битный формат преобразования универсального набора символов.

Как работает UTF-8

UTF-8 (8-битный формат преобразования Unicode) — одна из наиболее широко используемых схем кодирования для представления символов Unicode. В UTF-8 используется подход кодирования переменной длины, который позволяет эффективно представлять символы из разных сценариев и языков, используя 1, 2, 3 или 4 байта.

Чтобы понять, как работает UTF-8, важно понять концепцию ведущих байтов и конечных битов. Кодировка символа в UTF-8 начинается с ведущего байта, который указывает количество байтов, используемых для этого символа. Количество старших битов, установленных в 1 в ведущем байте, говорит нам, сколько байтов следует за ним.

Например, для 2-байтового символа первый байт начинается с 110xxxxx, где «x» представляет собой биты, полученные из кодовой точки символа. Завершающие байты начинаются с 10xxxxxx, что указывает на то, что они содержат оставшиеся биты кодовой точки.

При кодировании символа в UTF-8:

  • Для 1-байтового символа (от U+0000 до U+007F) используется один байт, в котором старший бит равен 0, за которым следуют 7 бит кодовой точки символа.
  • Для 2-байтового символа (от U+0080 до U+07FF) используется начальный байт с 110xxxxx и конечный байт с 10xxxxxx.
  • Для 3-байтового символа (от U+0800 до U+FFFF) используются три байта с 1110xxxx, 10xxxxxx и 10xxxxxx.
  • Для 4-байтового символа (от U+10000 до U+10FFFF) используются четыре байта с 11110xxx, 10xxxxxx, 10xxxxxx и 10xxxxxx.

В каждой последовательности «x» представляет биты, полученные из кодовой точки символа. Начальные байты (начинающиеся с «110», «1110» или «11110») указывают, сколько байтов используется для кодирования символа, а последующие байты (начинающиеся с «10») содержат фактические биты кода. точка.

Чтобы проиллюстрировать концепцию кодировки UTF-8, приведем фрагмент кода Python, который преобразует символы Юникода в соответствующие им представления UTF-8:

def unicode_to_utf8(char):
    # Check if the character is within the 1-byte Unicode range (U+0000 to U+007F)
    if 0x0000 <= char <= 0x007F:
        # 1-byte character: U+0000 to U+007F
        utf8_data = bytes([char])
    
    # Check if the character is within the 2-byte Unicode range (U+0080 to U+07FF)
    elif 0x0080 <= char <= 0x07FF:
        # 2-byte character: U+0080 to U+07FF
        # Calculate the leading byte and trailing byte values
        leading_byte = 0xC0 | ((char >> 6) & 0x1F)  # Leading byte starts with 110xxxxx
        trailing_byte = 0x80 | (char & 0x3F)        # Trailing byte starts with 10xxxxxx
        # Combine the leading and trailing bytes
        utf8_data = bytes([leading_byte, trailing_byte])
    
    # Check if the character is within the 3-byte Unicode range (U+0800 to U+FFFF)
    elif 0x0800 <= char <= 0xFFFF:
        # 3-byte character: U+0800 to U+FFFF
        # Calculate the leading bytes and trailing byte values
        leading_byte1 = 0xE0 | ((char >> 12) & 0x0F)  # Leading byte 1 starts with 1110xxxx
        leading_byte2 = 0x80 | ((char >> 6) & 0x3F)   # Leading byte 2 starts with 10xxxxxx
        trailing_byte = 0x80 | (char & 0x3F)          # Trailing byte starts with 10xxxxxx
        # Combine the leading and trailing bytes
        utf8_data = bytes([leading_byte1, leading_byte2, trailing_byte])
    
    # Check if the character is within the 4-byte Unicode range (U+10000 to U+10FFFF)
    elif 0x10000 <= char <= 0x10FFFF:
        # 4-byte character: U+10000 to U+10FFFF
        # Calculate the leading bytes and trailing byte values
        leading_byte1 = 0xF0 | ((char >> 18) & 0x07)   # Leading byte 1 starts with 11110xxx
        leading_byte2 = 0x80 | ((char >> 12) & 0x3F)  # Leading byte 2 starts with 10xxxxxx
        leading_byte3 = 0x80 | ((char >> 6) & 0x3F)   # Leading byte 3 starts with 10xxxxxx
        trailing_byte = 0x80 | (char & 0x3F)          # Trailing byte starts with 10xxxxxx
        # Combine the leading and trailing bytes
        utf8_data = bytes([leading_byte1, leading_byte2, leading_byte3, trailing_byte])
    
    else:
        return None  # Character is outside the valid Unicode range

    return utf8_data

# Example: Convert Unicode characters to UTF-8
unicode_chars = [0x65, 0x03B1, 0x144c, 0x1f600]  # Some sample Unicode characters
for char in unicode_chars:
    utf8_data = unicode_to_utf8(char)
    if utf8_data:
        print(f"Unicode U+{char:04X} to UTF-8 Encoding: {utf8_data.hex()}")
    else:
        print(f"Character U+{char:04X} is outside the valid Unicode range.")

Предоставленный код преобразует символы Unicode в кодировку UTF-8, как показано в выводе ниже:

Unicode U+0065 to UTF-8 Encoding: 65
Unicode U+03B1 to UTF-8 Encoding: ceb1
Unicode U+144C to UTF-8 Encoding: e1918c
Unicode U+1F600 to UTF-8 Encoding: f09f9880

Чтобы дополнительно проиллюстрировать концепцию кодировки UTF-8 и ее связь с Unicode, давайте рассмотрим 4-байтовый символ UTF-8.

На первом изображении ниже показан 4-байтовый символ UTF-8 «f09f9880» и соответствующее ему байтовое представление. Каждый байт представлен в шестнадцатеричном формате. В этом примере «f0», «9f», «98» и «80» — это четыре байта, составляющие кодировку символа UTF-8. Понимание того, как символы кодируются в байты, имеет основополагающее значение при работе с кодировкой символов в Python.

На втором изображении ниже показан символ Юникода «U+1F600» вместе с его байтовыми представлениями в UTF-8 и UTF-32.

  • Представление UTF-8 «f09f9880» показывает, как этот символ кодируется с использованием UTF-8, где каждый байт имеет шестнадцатеричную форму.
  • Представление UTF-32 «0001F600» отображает полное 32-битное представление кодовой точки Юникода для символа.