mirror of
https://github.com/krahets/hello-algo.git
synced 2026-07-08 03:56:47 +08:00
deploy
This commit is contained in:
@@ -4423,9 +4423,9 @@
|
||||
|
||||
<!-- Page content -->
|
||||
<h1 id="34">3.4 Кодирование символов *<a class="headerlink" href="#34" title="Permanent link">¶</a></h1>
|
||||
<p>В компьютере все данные хранятся в виде двоичных чисел, и символьный тип данных <code>char</code> не является исключением. Для представления символов необходимо задать "таблицу символов", которая устанавливает взаимно-однозначное соответствие между каждым символом и двоичным числом. С помощью этой таблицы компьютер может преобразовывать двоичные числа в символы.</p>
|
||||
<p>В компьютере все данные хранятся в виде двоичных чисел, и символьный тип данных <code>char</code> не является исключением. Для представления символов необходимо задать «таблицу символов», которая устанавливает взаимно-однозначное соответствие между каждым символом и двоичным числом. С помощью этой таблицы компьютер может преобразовывать двоичные числа в символы.</p>
|
||||
<h2 id="341-ascii">3.4.1 Таблица символов ASCII<a class="headerlink" href="#341-ascii" title="Permanent link">¶</a></h2>
|
||||
<p><u>Код ASCII</u> - это самая ранняя таблица символов; ее полное название - American Standard Code for Information Interchange (американский стандартный код обмена информацией). Для представления символов в ней используются 7 двоичных битов (нижние 7 битов одного байта), что позволяет закодировать до 128 различных символов. Как показано на рисунке 3-6, ASCII включает заглавные и строчные буквы английского алфавита, цифры 0 ~ 9, некоторые знаки препинания, а также некоторые управляющие символы (например перевод строки и табуляцию).</p>
|
||||
<p><u>Код ASCII</u> - это самая ранняя таблица символов. Ее полное название - American Standard Code for Information Interchange (американский стандартный код обмена информацией). Для представления символов в ней используются 7 двоичных битов (нижние 7 битов одного байта), что позволяет закодировать до 128 различных символов. Как показано на рисунке 3-6, ASCII включает заглавные и строчные буквы английского алфавита, цифры 0 ~ 9, некоторые знаки препинания, а также некоторые управляющие символы (например перевод строки и табуляцию).</p>
|
||||
<p><img alt="Таблица ASCII" class="animation-figure" src="../character_encoding.assets/ascii_table.png" /></p>
|
||||
<p align="center"> Рисунок 3-6 Таблица ASCII </p>
|
||||
|
||||
@@ -4435,11 +4435,11 @@
|
||||
<p>Позже люди обнаружили, что <strong>кодов EASCII все равно недостаточно для количества символов во многих языках</strong>. Например, китайских иероглифов существует почти сто тысяч, а в повседневном употреблении нужны тысячи. В 1980 году Государственное управление стандартов Китая выпустило таблицу символов <u>GB2312</u>, включающую 6763 иероглифа, что в основном удовлетворило потребности компьютерной обработки китайского текста.</p>
|
||||
<p>Однако GB2312 не умеет работать с некоторыми редкими иероглифами и традиционными формами письма. Таблица символов <u>GBK</u> представляет собой расширение GB2312 и в общей сложности содержит 21886 иероглифов. В схеме кодирования GBK символы ASCII представляются одним байтом, а китайские иероглифы - двумя байтами.</p>
|
||||
<h2 id="343-unicode">3.4.3 Таблица символов Unicode<a class="headerlink" href="#343-unicode" title="Permanent link">¶</a></h2>
|
||||
<p>С бурным развитием компьютерной техники таблицы символов и стандарты кодирования начали стремительно множиться, и это породило множество проблем. С одной стороны, такие таблицы обычно определяли символы только для конкретных языков и не могли нормально работать в многоязычной среде. С другой стороны, для одного и того же языка существовало несколько стандартов кодирования; если две машины использовали разные стандарты, при обмене информацией возникали искажения текста.</p>
|
||||
<p>С бурным развитием компьютерной техники таблицы символов и стандарты кодирования начали стремительно множиться, и это породило множество проблем. С одной стороны, такие таблицы обычно определяли символы только для конкретных языков и не могли нормально работать в многоязычной среде. С другой стороны, для одного и того же языка существовало несколько стандартов кодирования. Если две машины использовали разные стандарты, при обмене информацией возникали искажения текста.</p>
|
||||
<p>Исследователи той эпохи задумались: <strong>если создать достаточно полную таблицу символов, которая включит все языки и знаки мира, разве это не решит проблемы многоязычной среды и искаженного текста</strong>? Под влиянием этой идеи и появилась большая и всеобъемлющая таблица символов Unicode.</p>
|
||||
<p><u>Unicode</u> по-китайски называется "единый код" и теоретически способен вместить более миллиона символов. Его цель - собрать символы со всего мира в единую таблицу символов, предоставить универсальный стандарт для обработки и отображения текстов на разных языках и уменьшить количество проблем с искажением текста, вызванных различиями стандартов кодирования. С момента публикации в 1991 году Unicode непрерывно расширялся, добавляя новые языки и символы. По состоянию на сентябрь 2022 года Unicode уже включал 149186 символов, в том числе буквы разных языков, знаки, а также эмодзи.</p>
|
||||
<p>Как универсальный набор символов, Unicode по сути присваивает каждому символу уникальную "кодовую точку" (числовой идентификатор символа), диапазон которой составляет от U+0000 до U+10FFFF, образуя единое пространство нумерации символов. Однако <strong>Unicode не определяет, как именно хранить эти кодовые точки в компьютере</strong>. Тут неизбежно возникает вопрос: если в одном тексте одновременно встречаются кодовые точки Unicode разной длины, как система должна разбирать символы? Например, если дан код длиной 2 байта, как понять, является ли это одним 2-байтовым символом или двумя 1-байтовыми?</p>
|
||||
<p>Для этой проблемы <strong>прямолинейное решение состоит в том, чтобы хранить все символы в кодировке одинаковой длины</strong>. Как показано на рисунке 3-7, каждый символ в "Hello" занимает 1 байт, а каждый символ в "алгоритм" занимает 2 байта. Мы можем дополнить старшие биты нулями и закодировать все символы в "Hello алгоритм" в виде 2-байтовых единиц. Тогда система сможет считывать по одному символу каждые 2 байта и восстановить эту фразу.</p>
|
||||
<p><u>Unicode</u> по-китайски называется «единый код» и теоретически способен вместить более миллиона символов. Его цель - собрать символы со всего мира в единую таблицу символов, предоставить универсальный стандарт для обработки и отображения текстов на разных языках и уменьшить количество проблем с искажением текста, вызванных различиями стандартов кодирования. С момента публикации в 1991 году Unicode непрерывно расширялся, добавляя новые языки и символы. По состоянию на сентябрь 2022 года Unicode уже включал 149186 символов, в том числе буквы разных языков, знаки, а также эмодзи.</p>
|
||||
<p>Как универсальный набор символов, Unicode по сути присваивает каждому символу уникальную «кодовую точку» (числовой идентификатор символа), диапазон которой составляет от U+0000 до U+10FFFF, образуя единое пространство нумерации символов. Однако <strong>Unicode не определяет, как именно хранить эти кодовые точки в компьютере</strong>. Тут неизбежно возникает вопрос: если в одном тексте одновременно встречаются кодовые точки Unicode разной длины, как система должна разбирать символы? Например, если дан код длиной 2 байта, как понять, является ли это одним 2-байтовым символом или двумя 1-байтовыми?</p>
|
||||
<p>Для этой проблемы <strong>прямолинейное решение состоит в том, чтобы хранить все символы в кодировке одинаковой длины</strong>. Как показано на рисунке 3-7, каждый символ в «Hello» занимает 1 байт, а каждый символ в «алгоритм» занимает 2 байта. Мы можем дополнить старшие биты нулями и закодировать все символы в «Hello алгоритм» в виде 2-байтовых единиц. Тогда система сможет считывать по одному символу каждые 2 байта и восстановить эту фразу.</p>
|
||||
<p><img alt="Пример кодирования Unicode" class="animation-figure" src="../character_encoding.assets/unicode_hello_algo.png" /></p>
|
||||
<p align="center"> Рисунок 3-7 Пример кодирования Unicode </p>
|
||||
|
||||
@@ -4449,9 +4449,9 @@
|
||||
<p>Правила кодирования UTF-8 не слишком сложны и делятся на два случая.</p>
|
||||
<ul>
|
||||
<li>Для символов длиной 1 байт старший бит устанавливается в <span class="arithmatex">\(0\)</span> , а оставшиеся 7 битов содержат кодовую точку Unicode. Стоит отметить, что символы ASCII занимают первые 128 кодовых точек в наборе Unicode. Иными словами, <strong>кодировка UTF-8 обратно совместима с ASCII</strong>. Это означает, что мы можем использовать UTF-8 для разбора очень старых ASCII-текстов.</li>
|
||||
<li>Для символов длиной <span class="arithmatex">\(n\)</span> байт (где <span class="arithmatex">\(n > 1\)</span>) старшие <span class="arithmatex">\(n\)</span> битов первого байта устанавливаются в <span class="arithmatex">\(1\)</span> , а <span class="arithmatex">\((n + 1)\)</span>-й бит устанавливается в <span class="arithmatex">\(0\)</span> ; начиная со второго байта, старшие 2 бита каждого байта устанавливаются в <span class="arithmatex">\(10\)</span> ; все остальные биты используются для заполнения кодовой точки Unicode соответствующего символа.</li>
|
||||
<li>Для символов длиной <span class="arithmatex">\(n\)</span> байт (где <span class="arithmatex">\(n > 1\)</span>) старшие <span class="arithmatex">\(n\)</span> битов первого байта устанавливаются в <span class="arithmatex">\(1\)</span> , а <span class="arithmatex">\((n + 1)\)</span>-й бит устанавливается в <span class="arithmatex">\(0\)</span>. Начиная со второго байта, старшие 2 бита каждого байта устанавливаются в <span class="arithmatex">\(10\)</span>. Все остальные биты используются для заполнения кодовой точки Unicode соответствующего символа.</li>
|
||||
</ul>
|
||||
<p>На рисунке 3-8 показана UTF-8-кодировка для строки "Hello алгоритм". Можно заметить, что поскольку старшие <span class="arithmatex">\(n\)</span> битов установлены в <span class="arithmatex">\(1\)</span> , система может определить длину символа как <span class="arithmatex">\(n\)</span> , подсчитав число ведущих единиц.</p>
|
||||
<p>На рисунке 3-8 показана UTF-8-кодировка для строки «Hello алгоритм». Можно заметить, что поскольку старшие <span class="arithmatex">\(n\)</span> битов установлены в <span class="arithmatex">\(1\)</span> , система может определить длину символа как <span class="arithmatex">\(n\)</span> , подсчитав число ведущих единиц.</p>
|
||||
<p>Но почему старшие 2 бита всех остальных байтов устанавливаются в <span class="arithmatex">\(10\)</span> ? На самом деле это <span class="arithmatex">\(10\)</span> играет роль контрольного маркера. Если система начнет разбирать текст с неверного байта, префикс <span class="arithmatex">\(10\)</span> поможет быстро обнаружить аномалию.</p>
|
||||
<p>Причина выбора <span class="arithmatex">\(10\)</span> в качестве контрольного маркера в том, что по правилам UTF-8 символ не может иметь старшие два бита, равные <span class="arithmatex">\(10\)</span> . Это можно доказать от противного: если предположить, что у некоторого символа старшие два бита равны <span class="arithmatex">\(10\)</span> , то длина такого символа должна быть 1 байт, то есть это ASCII. Но у ASCII старший бит обязан быть <span class="arithmatex">\(0\)</span> , что противоречит предположению.</p>
|
||||
<p><img alt="Пример кодировки UTF-8" class="animation-figure" src="../character_encoding.assets/utf-8_hello_algo.png" /></p>
|
||||
@@ -4459,10 +4459,10 @@
|
||||
|
||||
<p>Помимо UTF-8, распространены еще два следующих способа кодирования.</p>
|
||||
<ul>
|
||||
<li><strong>Кодировка UTF-16</strong>: использует 2 или 4 байта для представления символа. Все символы ASCII и часто используемые неанглийские символы представляются 2 байтами; небольшая часть символов требует 4 байта. Для 2-байтовых символов кодировка UTF-16 совпадает с кодовой точкой Unicode.</li>
|
||||
<li><strong>Кодировка UTF-16</strong>: использует 2 или 4 байта для представления символа. Все символы ASCII и часто используемые неанглийские символы представляются 2 байтами. Небольшая часть символов требует 4 байта. Для 2-байтовых символов кодировка UTF-16 совпадает с кодовой точкой Unicode.</li>
|
||||
<li><strong>Кодировка UTF-32</strong>: каждый символ занимает 4 байта. Это означает, что UTF-32 требует больше места, чем UTF-8 и UTF-16, особенно в текстах с большой долей ASCII-символов.</li>
|
||||
</ul>
|
||||
<p>С точки зрения занимаемого места UTF-8 очень эффективна для английских символов, потому что им нужен всего 1 байт; а для некоторых неанглийских символов (например китайских) UTF-16 может быть эффективнее, потому что ей требуется только 2 байта, тогда как UTF-8 может потребовать 3 байта.</p>
|
||||
<p>С точки зрения занимаемого места UTF-8 очень эффективна для английских символов, потому что им нужен всего 1 байт. А для некоторых неанглийских символов (например китайских) UTF-16 может быть эффективнее, потому что ей требуется только 2 байта, тогда как UTF-8 может потребовать 3 байта.</p>
|
||||
<p>С точки зрения совместимости у UTF-8 наилучшая универсальность, и многие инструменты и библиотеки в первую очередь поддерживают именно UTF-8.</p>
|
||||
<h2 id="345">3.4.5 Кодирование символов в языках программирования<a class="headerlink" href="#345" title="Permanent link">¶</a></h2>
|
||||
<p>Для большинства языков программирования прошлого строки во время выполнения программы использовали фиксированные по длине кодировки, такие как UTF-16 или UTF-32. При кодировке фиксированной длины строку можно обрабатывать как массив, и такой подход дает следующие преимущества.</p>
|
||||
@@ -4473,14 +4473,14 @@
|
||||
</ul>
|
||||
<p>Вообще говоря, проектирование схем кодирования символов в языках программирования - очень интересная тема, в которой учитывается множество факторов.</p>
|
||||
<ul>
|
||||
<li>Тип <code>String</code> в Java использует кодировку UTF-16, и каждый символ занимает 2 байта. Это связано с тем, что на раннем этапе проектирования Java считалось, что 16 битов достаточно для представления всех возможных символов. Но это оказалось неверным предположением. Позднее Unicode вышел за пределы 16 битов, поэтому символы в Java теперь могут представляться парой 16-битных значений (так называемой "суррогатной парой").</li>
|
||||
<li>Тип <code>String</code> в Java использует кодировку UTF-16, и каждый символ занимает 2 байта. Это связано с тем, что на раннем этапе проектирования Java считалось, что 16 битов достаточно для представления всех возможных символов. Но это оказалось неверным предположением. Позднее Unicode вышел за пределы 16 битов, поэтому символы в Java теперь могут представляться парой 16-битных значений (так называемой «суррогатной парой»).</li>
|
||||
<li>Строки в JavaScript и TypeScript используют UTF-16 по причинам, похожим на Java. Когда Netscape впервые выпустила JavaScript в 1995 году, Unicode еще находился на ранней стадии развития, и 16-битного кодирования тогда было достаточно для представления всех символов Unicode.</li>
|
||||
<li>C# использует UTF-16 главным образом потому, что платформа .NET была разработана Microsoft, а многие технологии Microsoft (включая Windows) широко используют именно UTF-16.</li>
|
||||
</ul>
|
||||
<p>Из-за недооценки общего числа символов перечисленным выше языкам пришлось использовать "суррогатные пары" для представления Unicode-символов длиной больше 16 бит. Это вынужденный компромисс. С одной стороны, в строках с суррогатными парами один символ может занимать 2 байта или 4 байта, из-за чего теряется преимущество кодировки фиксированной длины. С другой стороны, обработка суррогатных пар требует дополнительного кода, что повышает сложность разработки и отладки.</p>
|
||||
<p>Из-за недооценки общего числа символов перечисленным выше языкам пришлось использовать «суррогатные пары» для представления Unicode-символов длиной больше 16 бит. Это вынужденный компромисс. С одной стороны, в строках с суррогатными парами один символ может занимать 2 байта или 4 байта, из-за чего теряется преимущество кодировки фиксированной длины. С другой стороны, обработка суррогатных пар требует дополнительного кода, что повышает сложность разработки и отладки.</p>
|
||||
<p>По этим причинам некоторые языки программирования предложили иные схемы кодирования.</p>
|
||||
<ul>
|
||||
<li><code>str</code> в Python использует Unicode и гибкое строковое представление, где длина хранимого символа зависит от наибольшей кодовой точки Unicode в строке. Если все символы строки принадлежат ASCII, каждый символ занимает 1 байт; если есть символы за пределами ASCII, но все они лежат в базовой многоязычной плоскости (BMP), каждый символ занимает 2 байта; если встречаются символы за пределами BMP, каждый символ занимает 4 байта.</li>
|
||||
<li><code>str</code> в Python использует Unicode и гибкое строковое представление, где длина хранимого символа зависит от наибольшей кодовой точки Unicode в строке. Если все символы строки принадлежат ASCII, каждый символ занимает 1 байт. Если есть символы за пределами ASCII, но все они лежат в базовой многоязычной плоскости (BMP), каждый символ занимает 2 байта. Если встречаются символы за пределами BMP, каждый символ занимает 4 байта.</li>
|
||||
<li>Тип <code>string</code> в Go внутри использует кодировку UTF-8. Язык Go также предоставляет тип <code>rune</code>, предназначенный для представления одной кодовой точки Unicode.</li>
|
||||
<li>Типы <code>str</code> и <code>String</code> в Rust внутри используют UTF-8. В Rust также есть тип <code>char</code>, представляющий одну кодовую точку Unicode.</li>
|
||||
</ul>
|
||||
|
||||
Reference in New Issue
Block a user