Сколько бит памяти требуется для кодирования одного символа в кодировке Windows

Обновлено: 04.07.2024

Я немного запутался в кодировках. Насколько я знаю, старые символы ASCII занимали один байт на символ. Сколько байтов требуется для символа Unicode?

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

А что означают UTF-7, UTF-6, UTF-16 и т. д.? Это разные версии Unicode?

Я прочитал статью в Википедии о Unicode, но для меня это довольно сложно. Я с нетерпением жду простого ответа.

Извините, простого ответа нет. Я нахожу все это немного беспорядком. Утверждалось, что Unicode использует два байта и может представлять все символы, но оказалось, что двух байтов недостаточно.

"Простой ответ": символ Юникода занимает 1-4 байта. Юникод охватывает множество языков, но не все. В прошлый раз, когда я смотрел, например, клингон не был официальным набором символов Unicode.

Клингон не является частью самого стандарта Unicode, нет. Вместо этого используется зона частного использования Uniode (U+F8D0 - U+F8FF).

Спасибо, вопрос - спасибо. Моя ситуация заключается в хранении данных через LMS, совместимые со SCORM 1.2. стандарт для SCORM 1.2 'cmi.suspend_data' составляет 4096 байт данных, что, по предположению предыдущего разработчика, означает, что мы можем хранить 4096 символов. О, чувак, он ошибался - я только что обнаружил, почему наши закладки не работают на длинных курсах. Итак, теперь я знаю, что поскольку мы используем UTF-8, для каждого символа требуется 4 байта, что дает нам 1024 символа.

12 ответов 12

Как ни странно, никто не указал, как рассчитать, сколько байт занимает один символ Unicode. Вот правило для строк в кодировке UTF-8:

Итак, быстрый ответ: он занимает от 1 до 4 байтов, в зависимости от первого, который указывает, сколько байтов он займет.


Большое спасибо! Я просто просматривал стандарт IETF и ничего не нашел о кодировании, а в статье, которую я читал, не было достаточно подробностей, чтобы сказать, сколько битов используется для представления количества завершающего кода. баллов за "символ".

Теперь это находится на второй странице моей шпаргалки "Введение для новых членов команды" вместе с первыми двумя забавными комментариями

0xF4 был не ошибкой, а уточнением. Кодовые точки Unicode находятся в диапазоне 0–0x10ffff, поэтому последняя кодовая точка кодируется как F4 8F BF BF.

@DJPJ В принципе вы правы, но UTF-8 не использует все доступное пространство для совместимости с UTF-16.

Вы не увидите простого ответа, потому что его нет.

Во-первых, Unicode не содержит "каждый символ из каждого языка", хотя и пытается это сделать.

Юникод сам по себе является отображением, он определяет кодовые точки, а кодовая точка — это число, связанное обычно с символом. Я говорю обычно, потому что есть такие понятия, как объединение символов. Возможно, вы знакомы с такими вещами, как акценты или умляуты. Их можно использовать с другим символом, таким как a или u, для создания нового логического символа. Таким образом, символ может состоять из 1 или более кодовых точек.

Чтобы быть полезными в вычислительных системах, нам нужно выбрать представление для этой информации. Это различные кодировки Unicode, такие как utf-8, utf-16le, utf-32 и т. д. Они отличаются в основном размером своих кодовых единиц. UTF-32 — самая простая кодировка, она имеет 32-битную кодовую единицу, что означает, что отдельная кодовая точка удобно вписывается в кодовую единицу. В других кодировках будут ситуации, когда для кодовой точки потребуется несколько кодовых единиц или эта конкретная кодовая точка вообще не может быть представлена ​​в кодировке (это проблема, например, с UCS-2).

Из-за гибкости комбинирования символов даже в пределах данной кодировки количество байтов на символ может варьироваться в зависимости от символа и формы нормализации. Это протокол для работы с символами, которые имеют более одного представления (вы можете сказать «a» с акцентом, который представляет собой 2 кодовых точки, один из которых является комбинацией символов или «a» с ударением, который является одной кодовой точкой). ).

Начиная с конца 60-х годов компьютеры все чаще использовались для обработки текстовой информации, и в настоящее время большинство персональных компьютеров в мире (и большую часть времени) заняты обработкой текстовой информации.

ASCII — базовая кодировка текста для латиницы

Традиционно для кодирования одного символа используется количество информации, равное 1 байту, то есть I = 1 байт = 8 бит.

Для кодирования одного символа требуется 1 байт информации. Если рассматривать символы как возможные события, то можно подсчитать, сколько различных символов можно закодировать: N = 2I = 28 = 256.

Такого количества символов вполне достаточно для представления текстовой информации, включая прописные и строчные буквы русского и латинского алфавитов, цифры, знаки, графические символы и т.п. двоичный код от 00000000 до 11111111.

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

Пользователь нажимает клавишу с символом на клавиатуре, и на компьютер отправляется определенная последовательность из восьми электрических импульсов (двоичный код символа). Код символа хранится в оперативной памяти компьютера, где занимает один байт. В процессе отображения символа на экране компьютера выполняется обратный процесс - декодирование, то есть преобразование кода символа в его образ. Кодовая таблица ASCII (американский стандартный код для обмена информацией) была принята в качестве международного стандарта. Стандартная таблица частей ASCII Важно, чтобы присвоение определенного кода символу было предметом соглашения, которое фиксируется в таблице кодов. Первые 33 кода (от 0 до 32) соответствуют не символам, а операциям (перевод строки, ввод пробела и так далее). Коды с 33 по 127 являются международными и соответствуют символам латинского алфавита, цифрам, арифметическим знакам и знакам препинания. Коды от 128 до 255 являются национальными, то есть одному и тому же коду в национальных кодировках соответствуют разные символы.

К сожалению, в настоящее время существует пять различных кодовых таблиц для русских букв (KOI8, CP1251, CP866, Mac, ISO), поэтому тексты, созданные в одной кодировке, будут некорректно отображаться в другой.

В настоящее время действует новый международный стандарт Unicode, который выделяет на каждый символ не один байт, а два, поэтому с его помощью можно кодировать не 256 символов, а N = 216 = 65536 разных

Юникод — появление универсальной кодировки текста (UTF 32, UTF 16 и UTF 8)

Эти тысячи символов из языковой группы Юго-Восточной Азии не могли быть описаны в одном байте информации, который был выделен для кодирования символов в расширенных кодировках ASCII. В результате был создан консорциум под названием Юникод (Unicode — Unicode Consortium) при сотрудничестве многих лидеров ИТ-индустрии (тех, кто производит программное обеспечение, кто кодирует оборудование, кто создает шрифты), которые были заинтересованы в появлении универсальной кодировки текста.

Первой текстовой кодировкой, опубликованной под эгидой консорциума Unicode, была кодировка UTF 32. Число в названии кодировки UTF 32 означает количество битов, которые используются для кодирования одного символа. 32 бита — это 4 байта информации, которые потребуются для кодирования одного символа в новой универсальной кодировке UTF 32.

В результате один и тот же файл с текстом, закодированным в расширенной кодировке ASCII и в кодировке UTF 32, в последнем случае будет иметь размер (вес) в четыре раза больше. Это плохо, но теперь у нас есть возможность закодировать с помощью UTF 32 количество символов, равное двум в тридцатой степени (миллиарды символов, которые покроют любое действительно необходимое значение с колоссальным запасом).

А вот многим странам с языками европейской группы вообще не нужно было использовать такое огромное количество символов в кодировке, однако при использовании UTF 32 они даром получили четырехкратное увеличение веса. текстовых документов и, как следствие, увеличение объема интернет-трафика и объема хранимых данных. Это очень много и никто не мог себе позволить такую ​​растрату.

В результате разработки универсальной кодировки Unicode появилась UTF 16, которая оказалась настолько удачной, что была принята по умолчанию в качестве базового пространства для всех используемых нами символов. UTF 16 использует два байта для кодирования одного символа. Например, в операционной системе Windows можно пройти по пути Пуск — Программы — Стандартные — Служебные — Таблица символов.

В результате откроется таблица с векторными формами всех шрифтов, установленных в вашей системе. Если вы выберете набор символов Unicode в дополнительных параметрах, вы сможете увидеть для каждого шрифта отдельно весь ассортимент входящих в него символов. Кстати, нажав на любой из этих символов, можно увидеть его двухбайтный код в кодировке UTF 16, состоящий из четырех шестнадцатеричных цифр:


Сколько символов можно закодировать в кодировке UTF 16 с помощью 16 бит? 65 536 символов (два в степени шестнадцати) были приняты в качестве базового пространства в Unicode. Кроме того, существуют способы кодирования с помощью UTF 16 около двух миллионов символов, но они ограничены расширенным пространством в миллион символов текста.

Но даже удачная версия кодировки Unicode под названием UTF 16 не принесла особого удовлетворения тем, кто писал, например, программы только на английском языке, поскольку после перехода с расширенной версии кодировки ASCII на UTF 16, удвоился вес документов (один байт на один символ в ASCII и два байта на один и тот же символ в кодировке UTF 16). Именно для удовлетворения всех и каждого в консорциуме Unicode было решено придумать кодировку текста переменной длины.

Эта кодировка в Юникоде называлась UTF 8. Несмотря на восьмерку в названии, UTF 8 является полноценной кодировкой переменной длины, т.е. каждый символ текста можно закодировать в последовательность длиной от одного до шести байт. На практике в UTF 8 используется только диапазон от одного до четырех байт, т.к. дальше четырех байт кода ничего даже теоретически представить невозможно.

В UTF 8 все латинские символы кодируются одним байтом, как и в старой кодировке ASCII. Что примечательно, в случае кодирования только латиницей, даже те программы, которые не понимают Юникод, все равно будут читать то, что закодировано в UTF 8. То есть базовая часть кодировки ASCII перекочевала в UTF 8.

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

Теоретически решение этих проблем уже давно существует. Он называется Unicode Unicode. Это таблица кодирования, в которой для кодирования каждого символа используются 2 байта, т.е. 16 бит. На основе такой таблицы можно закодировать N = 2 16 = 65 536 символов.

Unicode включает почти все современные шрифты, в том числе: арабский, армянский, бенгальский, бирманский, греческий, грузинский, деванагари, иврит, кириллицу, коптский, кхмерский, латинский, тамильский, хангыль, ханьский (Китай, Япония, Корея), чероки. , эфиопский, японский (катакана, хирагана, кандзи) и другие.

Для академических целей было добавлено множество исторических шрифтов, в том числе древнегреческие, египетские иероглифы, клинопись, письмо майя, этрусский алфавит.

Юникод предоставляет широкий спектр математических и музыкальных символов и пиктограмм.

Для символов кириллицы в Unicode существует два диапазона кодов:

Но табличное внедрение Unicode в чистом виде сдерживается по той причине, что если код одного символа будет занимать не один байт, а два байта, то для его хранить текст, и в два раза больше времени на его передачу по каналам связи.

В закодированном английском тексте используется только 26 букв латинского алфавита и еще 6 знаков препинания. В этом случае текст, содержащий 1000 символов, можно гарантированно сжать без потери информации до размера:

Словарь Эллочки - "каннибалы" (персонаж романа "Двенадцать стульев") - 30 слов. Сколько бит хватит, чтобы закодировать весь словарный запас Эллочки? Варианты: 8, 5, 3, 1.

Единицы измерения объема данных и емкости памяти: килобайты, мегабайты, гигабайты.

Итак, мы выяснили, что в большинстве современных кодировок для хранения одного символа текста на электронном носителе выделяется 1 байт. Те. в байтах измеряется объем (V), занимаемый данными при их хранении и передаче (файлы, сообщения).

Объем данных (V) - количество байтов, необходимое для их хранения в памяти электронного носителя информации.

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

Однако байт — это маленькая единица измерения объема данных, более крупные — это килобайты, мегабайты, гигабайты, терабайты.

Следует помнить, что префиксы "кило", "мега", "гига" . не являются десятичными в этом случае. Так что «кило» в слове «килобайт» не означает «тысяча», т.е. не означает «10 3». Бит — это двоичная единица, и по этой причине в информатике удобно использовать единицы измерения, кратные числу «2», а не числу «10».

1 байт = 2 3 = 8 бит, 1 килобайт = 2 10 = 1024 байт. В двоичном формате 1 килобайт = & 1 000 000 000 байт.

Целевая аудитория: авторы контента, пользователи и все, кто не знает, что такое кодировка символов, и хочет получить краткую информацию о том, как она на них влияет.

Вопрос

Что такое кодировка символов и зачем мне это?

Ответить

Во-первых, какое мне дело?

Если вы используете какой-либо другой текст, кроме самого простого английского текста, люди могут не прочитать созданный вами контент, пока вы не скажете, какую кодировку символов вы использовали.

Например, вы можете сделать так, чтобы текст выглядел так:

но на самом деле это может выглядеть так:

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

Так что же такое кодировка символов?

Слова и предложения в тексте создаются из файлов . Примеры символов включают латинскую букву á, китайскую иероглифику 請 или иероглиф деванагари ह .

Возможно, вы не сможете увидеть некоторые символы на этой странице, потому что у вас нет необходимых шрифтов. Если вы нажмете на то место, где вы ожидали увидеть символ, вы перейдете к графической версии. Эта страница закодирована в UTF-8.

Символы, необходимые для определенной цели, группируются в (также называемые ). (Для однозначного обращения к символам каждый символ связан с числом, называемым .)

Символы хранятся в компьютере как один или несколько .

В принципе, вы можете визуализировать это, предположив, что все символы хранятся в компьютерах с использованием специального кода, подобного шифрам, используемым в шпионаже. A предоставляет ключ для разблокировки (т.е. взлома) кода. Это набор сопоставлений между байтами в компьютере и символами в наборе символов. Без ключа данные выглядят как мусор.

Этот вводящий в заблуждение термин часто используется для обозначения того, что на самом деле является кодировкой символов. Вы должны знать об этом использовании, но по возможности придерживайтесь термина кодировки символов.

Таким образом, когда вы вводите текст с помощью клавиатуры или каким-либо другим способом, кодировка символов сопоставляет выбранные вами символы с определенными байтами в памяти компьютера, а затем для отображения текста считывает байты обратно в символы.

К сожалению, существует множество различных наборов символов и кодировок, т.е. множество различных способов отображения между байтами, кодовыми точками и символами. В разделе «Дополнительная информация» для тех, кто интересуется, немного больше подробностей.

Однако в большинстве случаев вам не нужно знать подробности. Вам просто нужно быть уверенным, что вы прислушаетесь к советам в разделе Как это на меня повлияет? ниже.

Как в это вписываются шрифты?

A — это набор определений глифов, т.е. определения фигур, используемых для отображения символов.

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

Заданный шрифт обычно охватывает один набор символов или, в случае большого набора символов, такого как Unicode, только подмножество всех символов в наборе. Если в вашем шрифте нет глифа для определенного символа, некоторые браузеры или программные приложения будут искать отсутствующие глифы в других шрифтах в вашей системе (это будет означать, что глиф будет отличаться от окружающего текста, например, примечание о выкупе). ). В противном случае вы обычно увидите квадратную рамку, вопросительный знак или какой-либо другой символ. Например:

Как это влияет на меня?

В настоящее время как автор или разработчик контента вы всегда должны выбирать кодировку символов UTF-8 для своего контента или данных. Эта кодировка Unicode является хорошим выбором, потому что вы можете использовать кодировку одного символа для обработки любого символа, который вам может понадобиться. Это сильно упрощает дело. Использование Unicode во всей вашей системе также избавляет от необходимости отслеживать и преобразовывать различные кодировки символов.

Авторам контента необходимо выяснить, как объявить кодировку символов, используемую для формата документа, с которым они работают.

Обратите внимание, что простое объявление другой кодировки на вашей странице не изменит байты; вам также нужно сохранить текст в этой кодировке.

Разработчикам необходимо убедиться, что различные части системы могут взаимодействовать друг с другом, понимать, какие кодировки символов используются, и поддерживать все необходимые кодировки и символы. (В идеале вы должны использовать кодировку UTF-8 везде и избавитесь от этой проблемы.)

По приведенным ниже ссылкам можно найти дополнительную информацию по этим темам.

Дополнительная информация

В этом разделе содержится небольшая дополнительная информация о сопоставлении между байтами, кодовыми точками и символами для тех, кому это интересно. Не стесняйтесь просто перейти к разделу «Дополнительная литература».

Обратите внимание, что числа кодовых точек обычно выражаются в шестнадцатеричной системе счисления, т.е. основание 16. Например, 233 в шестнадцатеричной форме равно E9. Значения кодовой точки Unicode обычно записываются в форме U+00E9.

В наборе кодированных символов ISO 8859-1 (также известном как Latin1) значение десятичной кодовой точки для буквы é равно 233. Однако в ISO 8859-5 та же самая кодовая точка представляет кириллический символ щ .

Эти наборы символов содержат менее 256 символов и напрямую сопоставляют кодовые точки со значениями байтов, поэтому кодовая точка со значением 233 представлена ​​одним байтом со значением 233. Обратите внимание, что только контекст определяет, будет ли этот байт представляет либо é, либо sch .

Есть и другие способы обработки символов из ряда сценариев. Например, с помощью набора символов Unicode вы можете представлять оба символа в одном наборе. На самом деле Unicode содержит в одном наборе, вероятно, все символы, которые вам когда-либо понадобятся. В то время как буква é по-прежнему представлена ​​кодовой точкой 233, кириллический символ щ теперь имеет кодовую точку 1097.

В наши дни байты обычно состоят из 8 бит. Существует только 2 8 (т.е. 256) уникальных способов объединения 8 битов.

С другой стороны, 1097 — слишком большое число, чтобы его можно было представить одним байтом*. Итак, если вы используете кодировку символов для текста Unicode, называемую UTF-8, щ будет представлена ​​двумя байтами. Однако значение кодовой точки получается не просто из значения двух соединенных вместе байтов — требуется более сложное декодирование.

Другие символы Юникода соответствуют одному, трем или четырем байтам в кодировке UTF-8.

Кроме того, обратите внимание, что буква é также представлена ​​двумя байтами в UTF-8, а не одним байтом, который используется в ISO 8859-1. (Только символы ASCII кодируются одним байтом в UTF-8.)

UTF-8 — это наиболее широко используемый способ представления текста Unicode на веб-страницах, и вы всегда должны использовать UTF-8 при создании своих веб-страниц и баз данных. Но, в принципе, UTF-8 — это лишь один из возможных способов кодировки символов Юникода. Другими словами, одна кодовая точка в наборе символов Unicode может фактически отображаться в различные последовательности байтов, в зависимости от того, какая кодировка использовалась для документа. Кодовые точки Unicode могут быть сопоставлены с байтами с использованием любой из кодировок, называемых UTF-8, UTF-16 или UTF-32. Символ деванагари क с кодовой точкой 2325 (что равно 915 в шестнадцатеричной записи) будет представлен двумя байтами при использовании кодировки UTF-16 (09 15), тремя байтами при использовании UTF-8 (E0 A4 95) или четырьмя байтами. байт с кодировкой UTF-32 (00 00 09 15).

Могут быть дополнительные сложности помимо описанных в этом разделе (например, порядок байтов и escape-последовательности), но описанные здесь подробности показывают, почему важно, чтобы приложение, с которым вы работаете, знало, какая кодировка символов подходит для ваших данных. , и знает, как обращаться с этой кодировкой.

Дополнительная литература

Начинаете? Введение в наборы символов и кодировки — указывает на другие документы W3C, связанные с наборами символов и кодировками

Учебник, Работа с кодировками символов в HTML и CSS. Советы по выбору кодировки, ее объявлению и другим темам, связанным с HTML и CSS.

Настройка кодировки в веб-приложениях для разработки — как заставить ваш редактор сохранять в другой кодировке список сред редактирования.

Асгер Смидт< бр />

Во второй части мы познакомимся с двумя наиболее распространенными сегодня кодировками и рассмотрим некоторые их различия.

Различия между различными кодировками

Если бы в свое время вся ИТ-индустрия договорилась об общей кодировке, сейчас было бы намного проще с этим справляться. Тем не менее, это не так. На протяжении многих лет различные компании пытались решить одну и ту же проблему: как представить текст в виде двоичных данных для хранения или передачи. В результате сегодня существует огромное количество систем кодирования. К сожалению, многие из них почти идентичны, что заставляет еще больше усомниться в необходимости их существования.

Многие современные кодировки основаны на кодировке ASCII, но расширяют ее и включают больше символов. Это означает, что текст, который содержит только символы, присутствующие в стандарте ASCII, часто также может быть декодирован как ASCII, даже если технически он закодирован с использованием другого стандарта. Это связано с тем, что многие более крупные кодировки по-прежнему используют те же коды, что и ASCII, для первых 128 символов, но помимо этих символов эти схемы кодирования различаются в большей или меньшей степени.

Давайте сравним две наиболее распространенные кодировки, используемые для западных языков, Windows-1252 и UTF-8.

Windows-1252

Это кодировка по умолчанию, используемая системами Windows в большинстве западных стран. Это означает, что текстовые данные, созданные программным обеспечением, работающим в таких системах, по умолчанию будут использовать кодировку Windows-1252, если явно не указано другое.Некоторое программное обеспечение позволяет пользователю выбирать, какую кодировку использовать, другое настроено на использование определенной кодировки, а не кодировки по умолчанию, а третье оставляет это на усмотрение самой системы.

Windows-1252 — это однобайтовая кодировка, что означает, что каждый символ кодируется как один байт, как и в ASCII. Однако, поскольку Windows-1252 использует полные 8 битов каждого байта для своих кодовых точек (в отличие от 7-битных кодов ASCII), она содержит 256 кодовых точек по сравнению со 128 кодовыми точками ASCII. Первая половина кодовых точек идентична определенные в ASCII, а вторая половина кодирует дополнительные символы, которых нет в наборе символов ASCII.

Этот дизайн означает, что большинство распространенных символов, используемых в западных языках, занимают только один байт пространства, а многобайтовые кодировки используются реже. В результате UTF-8 может кодировать любой символ, сохраняя при этом относительно небольшой размер данных. Это полезно как для постоянного хранения (небольшие размеры файлов), так и для передачи (например, при открытии веб-страницы). По этой причине UTF-8 в настоящее время является самой распространенной кодировкой, используемой во всемирной паутине, и по состоянию на сентябрь 2019 года на ее долю приходилось 94 % всех веб-страниц.

Сравнение кодировок

Давайте посмотрим на конкретном примере, чем эти две кодировки отличаются друг от друга. Мы будем использовать слово «Naïveté», которое содержит два символа, отличных от ASCII (у него есть альтернативные варианты написания без них, но в примере это признанное правильное написание слова на английском языке).


Как мы видим, символы ï и é существуют в обеих кодировках, но кодируются двумя разными способами. В Windows-1252 все символы кодируются с использованием одного байта, поэтому кодировка содержит всего 256 символов. Однако в UTF-8 эти два символа кодируются с использованием 2 байтов каждый. В результате в кодировке UTF-8 слово занимает на два байта больше, чем в кодировке Windows-1252.

Итак, разные кодировки обрабатывают некоторые символы по-разному. В следующей и третьей части этой серии блогов мы рассмотрим, как это может вызвать у нас проблемы. Найдите кодировку 101 – часть 3 здесь.

Термин символ используется здесь в общем смысле то, что читатель воспринимает как отдельный элемент отображения. Распространенными примерами являются буква «а», символ «@» и эмодзи «🐂». Иногда то, что выглядит как один символ, на самом деле состоит из нескольких независимых элементов отображения, как объясняется в разделе о кластерах графем.

Типы строк и символов

Экземпляр класса string представляет некоторый текст. Строка логически представляет собой последовательность 16-битных значений, каждое из которых является экземпляром структуры char. Свойство string.Length возвращает количество экземпляров char в экземпляре строки.

Следующий пример функции выводит значения в шестнадцатеричном формате всех экземпляров char в строке:

Передайте этой функции строку "Hello", и вы получите следующий вывод:

Каждый символ представлен одним значением char. Эта закономерность справедлива для большинства языков мира. Например, вот вывод для двух китайских иероглифов, которые звучат как nǐ hǎo и означают Привет:

Однако для некоторых языков и некоторых символов и эмодзи требуется два экземпляра char для представления одного символа. Например, сравните символы и экземпляры char в слове, означающем осейдж на языке осейджей:

В предыдущем примере каждый символ, кроме пробела, представлен двумя экземплярами char.

Один эмодзи Unicode также представлен двумя символами char, как показано в следующем примере, показывающем эмодзи быка:

Эти примеры показывают, что значение string.Length , указывающее количество экземпляров char, не обязательно указывает количество отображаемых символов. Один экземпляр char сам по себе не обязательно представляет символ.

Пары символов, которые соответствуют одному символу, называются суррогатными парами. Чтобы понять, как они работают, вам нужно понимать кодировку Unicode и UTF-16.

Кодовые точки Юникода

Юникод — это международный стандарт кодирования, используемый на различных платформах, с различными языками и алфавитами.

Стандарт Unicode определяет более 1,1 миллиона кодовых точек. Кодовая точка — это целое число, которое может находиться в диапазоне от 0 до U+10FFFF (десятичное число 1 114 111). Некоторые кодовые точки назначаются буквам, символам или эмодзи. Другие назначаются действиям, управляющим отображением текста или символов, например переходу на новую строку. Многие кодовые точки еще не назначены.

Вот несколько примеров присвоения кодовых точек со ссылками на таблицы Unicode, в которых они появляются:

Десятичный Шестнадцатеричный Пример Описание
10 U+000A Н/Д< /td> ПЕРЕД СТРОКИ
97 U+0061 a СТРОЧНАЯ ЛАТИНСКАЯ БУКВА A
562 U+0232 Ȳ ЛАТИНСКАЯ ЗАГЛАВНАЯ БУКВА Y С МАКРОНОМ
68 675 U+10C43 𐱃 СТАРО-ТЮРСКАЯ БУКВА ОРХОН АТ
127,801 U+1F339 🌹 эмодзи РОЗА

Кодовые точки обычно обозначаются с помощью синтаксиса U+xxxx , где xxxx – целочисленное значение в шестнадцатеричном формате.

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

  • Базовая многоязычная плоскость (BMP) в диапазоне от U+0000 до U+FFFF . Этот 16-битный диапазон обеспечивает 65 536 кодовых точек, что достаточно для охвата большинства систем письма в мире.
  • Дополнительные кодовые точки в диапазоне U+10000..U+10FFFF . Этот 21-битный диапазон содержит более миллиона дополнительных кодовых точек, которые можно использовать для менее известных языков и других целей, таких как эмодзи.

На следующей диаграмме показана взаимосвязь между BMP и дополнительными кодовыми точками.

Единицы кода UTF-16

Один 16-битный код может представлять любую кодовую точку в 16-битном диапазоне базовой многоязычной плоскости. Но для кодовой точки в дополнительном диапазоне необходимы два экземпляра char.

Суррогатные пары

Преобразование двух 16-битных значений в одно 21-битное облегчается специальным диапазоном, называемым суррогатными кодовыми точками, от U+D800 до U+DFFF (десятичные числа от 55 296 до 57 343). ) включительно.

На следующей диаграмме показана взаимосвязь между BMP и суррогатными кодовыми точками.

Когда за старшим заместителем кодовой точкой ( U+D800..U+DBFF ) сразу следует младшая суррогатная кодовая точка ( U+DC00..U+ DFFF ), пара интерпретируется как дополнительная кодовая точка по следующей формуле:

Вот та же формула в десятичной системе счисления:

старшая суррогатная кодовая точка не имеет большего числового значения, чем младшая суррогатная кодовая точка. Старшая суррогатная кодовая точка называется «старшей», потому что она используется для вычисления старших 10 бит диапазона 20-битной кодовой точки. Младшая суррогатная кодовая точка используется для вычисления младших 10 бит.

Например, фактическая кодовая точка, соответствующая суррогатной паре 0xD83C и 0xDF39, вычисляется следующим образом:

Вот тот же расчет с использованием десятичной системы счисления:

В предыдущем примере показано, что "\ud83c\udf39" – это кодировка UTF-16 упомянутой ранее кодовой точки U+1F339 ROSE ('🌹').

Скалярные значения Unicode

Термин скалярное значение Unicode относится ко всем кодовым точкам, кроме суррогатных кодовых точек. Другими словами, скалярное значение — это любая кодовая точка, которой назначен символ или может быть присвоен символ в будущем. Под «символом» здесь понимается все, что может быть назначено кодовой точке, включая такие вещи, как действия, управляющие отображением текста или символов.

На следующей диаграмме показаны кодовые точки скалярного значения.

Тип Rune как скалярное значение

Конструкторы Rune проверяют, является ли результирующий экземпляр допустимым скалярным значением Unicode, в противном случае они выдают исключение. В следующем примере показан код, который успешно создает экземпляры Rune, поскольку входные данные представляют допустимые скалярные значения:

В следующем примере возникает исключение, поскольку кодовая точка находится в суррогатном диапазоне и не является частью суррогатной пары:

В следующем примере возникает исключение, поскольку кодовая точка выходит за пределы дополнительного диапазона:

Пример использования рун: изменение регистра букв

API, который принимает char и предполагает, что он работает с кодовой точкой, являющейся скалярным значением, не работает правильно, если char получен из суррогатной пары. Например, рассмотрим следующий метод, который вызывает Char.ToUpperInvariant для каждого символа в строке:

Если входная строка содержит строчную букву Дезерет er ( 𐑉 ), этот код не преобразует ее в прописную ( 𐐡 ). Код вызывает char.ToUpperInvariant отдельно для каждой суррогатной кодовой точки, U+D801 и U+DC49 . Но U+D801 сам по себе не имеет достаточно информации, чтобы идентифицировать его как строчную букву, поэтому char.ToUpperInvariant оставляет его в покое. Точно так же он обрабатывает U+DC49. В результате строчная буква «𐑉» во входной строке не преобразуется в прописную «𐐡».

Вот два варианта правильного преобразования строки в верхний регистр:

Вызовите String.ToUpperInvariant для входной строки, а не итерируйте посимвольно. Строка.Метод ToUpperInvariant имеет доступ к обеим частям каждой суррогатной пары, поэтому он может правильно обрабатывать все кодовые точки Unicode.

Повторяйте скалярные значения Unicode как экземпляры Rune, а не экземпляры char, как показано в следующем примере. Поскольку экземпляр Rune является допустимым скалярным значением Unicode, его можно передать API-интерфейсам, которые должны работать со скалярным значением. Например, вызов Rune.ToUpperInvariant, как показано в следующем примере, дает правильные результаты:

Другие API рун

Тип Rune предоставляет аналоги многих API-интерфейсов char. Например, следующие методы отражают статические API для типа char:

Чтобы получить необработанное скалярное значение из экземпляра Rune, используйте свойство Rune.Value.

Чтобы преобразовать экземпляр Rune обратно в последовательность char, используйте метод Rune.ToString или Rune.EncodeToUtf16.

Поскольку любое скалярное значение Unicode может быть представлено одним символом или суррогатной парой, любой экземпляр Rune может быть представлен не более чем двумя экземплярами символов. Используйте Rune.Utf16SequenceLength, чтобы узнать, сколько экземпляров char требуется для представления экземпляра Rune.

Кластеры графем

Рассмотрите экземпляры строки "a", "á", "á" и " 👩🏽‍🚒". Если ваша операционная система обрабатывает их в соответствии со стандартом Unicode, каждый из этих строковых экземпляров отображается как отдельный текстовый элемент или кластер графем. Но последние два представлены более чем одной кодовой точкой скалярного значения.

Строка "a" представлена ​​одним скалярным значением и содержит один экземпляр char.

Строка "á" представлена ​​одним скалярным значением и содержит один экземпляр char.

Строка "á" выглядит так же, как "á", но представлена ​​двумя скалярными значениями и содержит два экземпляра char.

  • U+0061 СТРОЧНАЯ ЛАТИНСКАЯ БУКВА A
  • U+0301 ОБЪЕДИНЕНИЕ ОСТРОГО АКЦЕНТА

Наконец, строка " 👩🏽‍🚒 " представлена ​​четырьмя скалярными значениями и содержит семь экземпляров char.

  • U+1F469 ЖЕНЩИНА (дополнительный диапазон, требуется суррогатная пара)
  • U+1F3FD EMOJI MODIFIER FITZPATRICK TYPE-4 (дополнительный диапазон, требуется суррогатная пара)
  • U+200D СОЕДИНИТЕЛЬ НУЛЕВОЙ ШИРИНЫ
  • U+1F692 ПОЖАРНАЯ МАШИНА (дополнительный диапазон, требуется суррогатная пара)

В некоторых из предыдущих примеров, таких как комбинированный модификатор акцента или модификатор тона кожи, кодовая точка не отображается на экране как отдельный элемент. Скорее, он служит для изменения внешнего вида текстового элемента, который был перед ним. Эти примеры показывают, что может потребоваться несколько скалярных значений, чтобы составить то, что мы считаем одним «символом» или «кластером графем».

Пример: количество символов, рун и текстовых элементов

Пример: разделение экземпляров строки

При разделении строковых экземпляров избегайте разделения суррогатных пар и кластеров графем. Рассмотрим следующий пример неправильного кода, который намеревается вставлять разрывы строк через каждые 10 символов в строке:

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

Возможность повреждения данных не устраняется, если вы перечисляете экземпляры Rune (скалярные значения) вместо экземпляров char. Набор экземпляров Rune может составлять кластер графем, который охватывает границу в 10 символов. Если набор кластеров графем разделен, он не может быть правильно интерпретирован.

Лучше разбить строку, подсчитав кластеры графем или текстовые элементы, как в следующем примере:

UTF-8 и UTF-32

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

Вот несколько примеров, показывающих, как одна и та же кодовая точка Unicode представлена ​​в каждой из этих трех систем кодирования Unicode:

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

Окончание байтов

В архитектуре с прямым порядком байтов строка, состоящая из кодовых точек UTF-16 [ D801 DCCC ], будет размещаться в памяти как байты [ 0x01, 0xD8, 0xCC, 0xDC ] . В архитектуре с обратным порядком байтов эта же строка будет размещаться в памяти как байты [ 0xD8, 0x01, 0xDC, 0xCC ] .

Компьютерные системы, взаимодействующие друг с другом, должны согласовать представление данных, передаваемых по сети. Большинство сетевых протоколов используют UTF-8 в качестве стандарта при передаче текста, отчасти для того, чтобы избежать проблем, которые могут возникнуть при взаимодействии машины с прямым порядком байтов с машиной с прямым порядком байтов.Строка, состоящая из кодовых точек UTF-8 [ F0 90 93 8C ], всегда будет представлена ​​как байты [ 0xF0, 0x90, 0x93, 0x8C ] независимо от порядка следования байтов.

В предыдущем примере метод Encoding.UTF8.GetBytes декодирует строку UTF-16 обратно в серию скалярных значений Unicode, затем повторно кодирует эти скалярные значения в UTF-8 и помещает полученную последовательность в байт. множество. Метод Encoding.UTF8.GetString выполняет обратное преобразование, преобразуя массив байтов UTF-8 в строку UTF-16 .

Поскольку кодировка UTF-8 широко распространена в Интернете, может возникнуть соблазн считывать необработанные байты из сети и обрабатывать данные так, как если бы это была кодировка UTF-8. Тем не менее, вы должны подтвердить, что он действительно правильно сформирован. Вредоносный клиент может отправить в вашу службу неверный формат UTF-8. Если вы работаете с этими данными, как если бы они были правильно сформированы, это может привести к ошибкам или дырам в безопасности вашего приложения. Для проверки данных UTF-8 можно использовать такой метод, как Encoding.UTF8.GetString , который будет выполнять проверку при преобразовании входящих данных в строку .

Правильная кодировка

Правильно сформированная кодировка Unicode представляет собой строку единиц кода, которую можно однозначно и без ошибок декодировать в последовательность скалярных значений Unicode. Правильно сформированные данные можно свободно перекодировать между UTF-8, UTF-16 и UTF-32.

Вопрос о том, правильно ли сформирована последовательность кодирования, не связан с последовательностью байтов архитектуры машины. Неверно сформированная последовательность UTF-8 одинаково неправильно сформирована как на машинах с обратным порядком байтов, так и на машинах с прямым порядком байтов.

Вот несколько примеров неправильного кодирования:

В кодировке UTF-8 последовательность [ 6C C2 61 ] имеет неправильный формат, поскольку за C2 не может следовать 61 .

В UTF-32 последовательность [ 0011ABCD ] имеет неверный формат, поскольку 0011ABCD находится за пределами диапазона скалярных значений Unicode.

Неправильный литерал:

Подстрока, разделяющая суррогатную пару:

API, такие как Encoding.UTF8.GetString, никогда не возвращают неправильно сформированные экземпляры строк. Методы Encoding.GetString и Encoding.GetBytes обнаруживают неправильно сформированные последовательности во входных данных и выполняют замену символов при создании выходных данных. Например, если Encoding.ASCII.GetString(byte[]) видит во входных данных байт, отличный от ASCII (за пределами диапазона U+0000..U+007F), он вставляет '?' в возвращаемый экземпляр строки. Encoding.UTF8.GetString(byte[]) заменяет неправильно сформированные последовательности UTF-8 на U+FFFD REPLACEMENT CHARACTER ('�') в возвращаемом экземпляре строки. Дополнительные сведения см. в стандарте Unicode, разделы 5.22 и 3.9.

Встроенные классы Encoding также можно настроить таким образом, чтобы они выдавали исключение, а не выполняли подстановку символов при обнаружении неправильно сформированных последовательностей. Этот подход часто используется в приложениях, чувствительных к безопасности, где замена символов может быть неприемлемой.

Читайте также: