Encoding utf 8: Encoding.UTF8 Свойство (System.Text) | Microsoft Docs

Содержание

Значение —

Чтобы понять атрибут «encoding», вы должны понять разницу между байтами и символами .

Думайте о байтах как о числах от 0 до 255, в то время как символы-это такие вещи, как «a», «1» и «Ä». Набор всех доступных символов называется набором символов .

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

Большинство кодировок основаны на старом наборе символов и кодировке под названием ASCII, которая представляет собой один байт на символ (на самом деле всего 7 бит) и содержит 128 символов, включая множество распространенных символов, используемых в английском языке США.

Например, вот 6 символов в наборе символов ASCII, которые представлены значениями от 60 до 65.

Extract of ASCII Table 60-65
╔══════╦══════════════╗
║ Byte ║  Character   ║
╠══════╬══════════════║
║  60  ║      <       ║
║  61  ║      =       ║
║  62  ║      >       ║
║  63  ║      ?       ║
║  64  ║      @       ║
║  65  ║      A       ║
╚══════╩══════════════╝

В полном наборе ASCII наименьшее используемое значение равно нулю, а наибольшее-127 (оба они являются скрытыми управляющими символами).

Однако, как только вам понадобится больше символов, чем предоставляет базовый ASCII (например, буквы с акцентами, символы валюты, графические символы и т. Д.), ASCII не подходит, и вам нужно что-то более обширное. Вам нужно больше символов (другой набор символов), и вам нужна другая кодировка, так как 128 символов недостаточно, чтобы вместить все символы. Некоторые кодировки предлагают один байт (256 символов) или до шести байт.

Со временем было создано множество кодировок. В мире Windows существует CP1252, или ISO-8859-1, в то время как Linux пользователей, как правило, предпочитают UTF-8. Java использует UTF-16 изначально.

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

Например, в ISO 8859-1 он представлен одним байтом значения 226 , тогда как в UTF-8 это два байта: 195, 162 . Однако в ISO 8859-1 195, 162 будет состоять из двух символов, Ã,¢ .

Думайте о XML не как о последовательности символов, а как о последовательности байтов.

Представьте, что система, получающая XML, видит байты 195, 162 . Откуда он знает, что это за персонажи?

Чтобы система могла интерпретировать эти байты как фактические символы (и таким образом отображать их или преобразовывать в другую кодировку), ей необходимо знать кодировку, используемую в XML.

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

<?xml , это намного проще сделать.

Наконец, атрибут version указывает версию XML, которых на данный момент существует две (см. Википедию XML версий . Существуют небольшие различия между версиями, поэтому синтаксический анализатор XML должен знать, с чем он имеет дело. В большинстве случаев (во всяком случае, для носителей английского языка) достаточно версии 1.0.

Модуль ngx_http_charset_module

Модуль ngx_http_charset_module

Модуль ngx_http_charset_module добавляет указанную кодировку в поле “Content-Type” заголовка ответа. Кроме того, модуль может перекодировать данные из одной кодировки в другую с некоторыми ограничениями:

  • перекодирование осуществляется только в одну сторону — от сервера к клиенту,
  • перекодироваться могут только однобайтные кодировки
  • или однобайтные кодировки в UTF-8 и обратно.
Пример конфигурации
include        conf/koi-win;

charset        windows-1251;
source_charset koi8-r;
Директивы
Синтаксис: charset кодировка | off;
Умолчание:
charset off;
Контекст: http, server, location, if в location

Добавляет указанную кодировку в поле “Content-Type” заголовка ответа. Если эта кодировка отличается от указанной в директиве source_charset, то выполняется перекодирование.

Параметр off отменяет добавление кодировки в поле “Content-Type” заголовка ответа.

Кодировка может быть задана с помощью переменной:

charset $charset;

В этом случае необходимо, чтобы все возможные значения переменной присутствовали хотя бы один раз в любом месте конфигурации в виде директив charset_map, charset или source_charset. Для кодировок utf-8, windows-1251 и koi8-r для этого достаточно включить в конфигурацию файлы conf/koi-win, conf/koi-utf и conf/win-utf. Для других кодировок можно просто сделать фиктивную таблицу перекодировки, например:

charset_map iso-8859-5 _ { }

Кроме того, кодировка может быть задана в поле “X-Accel-Charset” заголовка ответа. Эту возможность можно запретить с помощью директив proxy_ignore_headers, fastcgi_ignore_headers, uwsgi_ignore_headers, scgi_ignore_headers и grpc_ignore_headers.

Синтаксис: charset_map кодировка1 кодировка2 { ... }
Умолчание:
Контекст: http

Описывает таблицу перекодирования из одной кодировки в другую. Таблица для обратного перекодирования строится на основании тех же данных. Коды символов задаются в шестнадцатеричном виде. Неописанные символы в пределах 80-FF заменяются на “?”. При перекодировании из UTF-8 символы, отсутствующие в однобайтной кодировке, заменяются на “

&#XXXX;”.

Пример:

charset_map koi8-r windows-1251 {
    C0 FE ; # small yu
    C1 E0 ; # small a
    C2 E1 ; # small b
    C3 F6 ; # small ts
    ...
}

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

charset_map koi8-r utf-8 {
    C0 D18E ; # small yu
    C1 D0B0 ; # small a
    C2 D0B1 ; # small b
    C3 D186 ; # small ts
    ...
}

Полные таблицы преобразования из koi8-r в windows-1251 и из koi8-r и windows-1251 в utf-8 входят в дистрибутив и находятся в файлах conf/koi-win, conf/koi-utf и conf/win-utf.

Синтаксис: charset_types
mime-тип ...;

Умолчание:
charset_types text/html text/xml text/plain text/vnd.wap.wml
application/javascript application/rss+xml;
Контекст: http, server, location

Эта директива появилась в версии 0.7.9.

Разрешает работу модуля в ответах с указанными MIME-типами в дополнение к “text/html”. Специальное значение “*” соответствует любому MIME-типу (0.8.29).

До версии 1.5.4 по умолчанию вместо MIME-типа “application/javascript” использовался “application/x-javascript”.
Синтаксис: override_charset on | off;
Умолчание:
override_charset off;
Контекст: http, server, location, if в location

Определяет, выполнять ли перекодирование для ответов, полученных от проксированного сервера или от FastCGI/uwsgi/SCGI/gRPC-сервера, если в ответах уже указана кодировка в поле “Content-Type” заголовка ответа. Если перекодирование разрешено, то в качестве исходной кодировки используется кодировка, указанная в полученном ответе.

Необходимо отметить, что если ответ был получен в подзапросе, то, независимо от значения директивы override_charset
, всегда выполняется перекодирование из кодировки ответа в кодировку основного запроса.
Синтаксис: source_charset кодировка;
Умолчание:
Контекст: http, server, location, if в location

Задаёт исходную кодировку ответа. Если эта кодировка отличается от указанной в директиве charset, то выполняется перекодирование.

android — Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8

Я использую Unity 2020, и когда я пытаюсь скомпилировать свой проект, я получаю следующие ошибки:

1)

> Configure project :launcher WARNING: The option 'android.enableR8' is deprecated and should not be used anymore. It will be removed in a future version of the Android Gradle plugin, and will no longer allow you to disable R8.
  Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8

 FAILURE: Build failed with an exception.

 * What went wrong:
 Execution failed for task ':unityLibrary:compileReleaseJavaWithJavac'.
 > Failed to query the value of task ':unityLibrary:compileReleaseJavaWithJavac' property 
 'options.generatedSourceOutputDirectory'.
> Querying the mapped value of map(java.io.File property(org.gradle.api.file.Directory, 
property(org.gradle.api.file.Directory, fixed(class 
org.gradle.api.internal.file.DefaultFilePropertyFactory$FixedDirectory, 

 H:\FinalPluginProjectTest\Temp\gradleOut\unityLibrary\build\generated\ap_generated_sources\release\out))) org.gradle.api.internal.file.DefaultFilePropertyFactory$ToFileTransformer@545c8bb4) before task ':unityLibrary:compileReleaseJavaWithJavac' has completed is not supported
 CommandInvokationFailure: Gradle build failed. 
 C:\Program Files\Java\jdk1.8.0_281\bin\java.exe -classpath "H:\gradle-7.0\lib\gradle-launcher- 
 7.0.jar" org.gradle.launcher.GradleMain "-Dorg.gradle.jvmargs=-Xmx4096m" "assembleRelease"

 stderr[
 Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8

 FAILURE: Build failed with an exception.

 * What went wrong:
 Execution failed for task ':unityLibrary:compileReleaseJavaWithJavac'.
 > Failed to query the value of task ':unityLibrary:compileReleaseJavaWithJavac' property 
 'options.generatedSourceOutputDirectory'.
 > Querying the mapped value of map(java.io.File property(org.gradle.api.file.Directory,       
 property(org.gradle.api.file.Directory, fixed(class 
 org.gradle.api.internal.file.DefaultFilePropertyFactory$FixedDirectory, 
        
  H:\FinalPluginProjectTest\Temp\gradleOut\unityLibrary\build\generated\ap_generated_sources\release\out))) org.gradle.api.internal.file.DefaultFilePropertyFactory$ToFileTransformer@545c8bb4) before task ':unityLibrary:compileReleaseJavaWithJavac' has completed is not supported

кто-нибудь знает, в чем проблема? Если нужно добавить информацию, пишите! Пожалуйста, помогите, если сможете, заранее спасибо!

Использование UTF-8 в HTTP заголовках – CUBA Platform

Как известно, HTTP 1.1 — это текстовой протокол передачи данных. HTTP сообщения закодированы, используя ISO-8859-1 (которую условно можно считать расширенной версией ASCII, содержащей умляуты, диакритику и другие символы, используемые в западноевропейских языках). При этом в теле сообщений можно использовать другую кодировку, которая должна быть обозначена в заголовке «Content-Type». Но что делать, если нам необходимо задать non-ASCII символы не в теле сообщения, а в самих заголовках? Наверное, самый распространенный кейс — это проставление имени файла в «Content-Disposition» заголовке. Это, казалось бы, довольно распространенная задача, но ее реализация не так очевидна.

TL;DR: Используйте кодировку, описанную в RFC 6266, для «Content-Disposition» и преобразуйте текст в латиницу (транслит) в остальных случаях.

Небольшая вводная в кодировки

В статье упоминаются и используются кодировки US-ASCII (часто именуемую просто ASCII), ISO-8859-1 и UTF-8. Это небольшая вводная в эти кодировки. Раздел предназначен для разработчиков, которые редко или совсем не работают с кодировками и успели подзабыть их. Если вы к ним не относитесь, то смело пропускайте раздел.

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

7 бит достаточно, чтобы представить любой ASCII символ.8 = 256 вариантов.

ISO-8859-1 — кодировка, предназначенная для западноевропейских языков. Содержит французскую диакритику, немецкие умляуты и т.д.

Кодировка содержит 256 символов и, таким образом, может быть представлена одним байтом. Первая половина (128 символов) полностью совпадает с ASCII. Таким образом, если первый бит = 0, то это обычный ASCII символ. Если 1, то это символ, специфичный для ISO-8859-1.

UTF-8 — одна из самых известных кодировок наравне с ASCII. Способна кодировать 1.112.064 символов. Размер каждого символа варьируется от 1-го до 4-х байт (раньше допускались значения до 6 байт).


Программа, работающая с этой кодировкой, определяет по первым битам, как много байтов входит в символ. Если октет начинается с 0, то символ представлен одним байтом. 110 — два байта, 1110 — три байта, 11110 — 4 байта.

Как и в случае с ISO-8859-1, первые 128 символов полностью соответствуют ASCII. Поэтому тексты, использующие только ASCII символы, будут абсолютно идентичны в бинарном представлении, вне зависимости от того, использовалась ли для кодирования US-ASCII, ISO-8859-1 или UTF-8.

Использование UTF-8 в теле сообщения

Прежде чем перейти к заголовкам, давайте быстро взглянем, как использовать UTF-8 в теле сообщений. Для этого используется заголовок «Content-Type».

Если «Content-Type» не задан, то браузер должен обрабатывать сообщения, как будто они написаны в ISO-8859-1. Браузер не должен пытаться отгадать кодировку и, тем более, игнорировать «Content-Type». Но, что реально отобразится в ситуации, когда «Content-Type» не передан, зависит от реализации браузера. Например, Firefox сделает согласно спецификации и прочитает сообщение, будто оно было закодировано в ISO-8859-1. Google Chrome, напротив, будет использовать кодировку операционной системы, которая для многих российских пользователей равна Windows-1251. В любом случае, если сообщение было в UTF-8, то оно будет отображено некорректно.

Проставляем UTF-8 сообщение в значение заголовка

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

Что будет, если просто взять и записать UTF-8 значение в значение заголовка? Мы видели, что такой трюк с телом сообщения приведет к тому, что значение будет просто прочитано в ISO-8859-1. Логично было бы предположить, что то же самое произойдет с заголовком. Но это не так. Фактически, во многих, если не в большинстве, случаях такое решение будет работать. Сюда включаются старые айфончики, IE11, Firefox, Google Chrome. Единственным из находящихся у меня под рукой браузеров, когда я писал эту статью, который не захотел работать с таким заголовком, является Edge.

Такое поведение не зафиксировано в спецификациях. Возможно, разработчики браузеров решили облегчить жизнь разработчиков и автоматически определять, что в заголовках сообщение закодировано в UTF-8. В общем-то, это не является такой сложной задачей. Смотрим на первый бит: если 0, то ASCII, если 1 — то, возможно, UTF-8.

Нет ли в этом случае пересечения с ISO-8859-1? На самом деле, практически нет. Возьмем для примера UTF-8 символ из 2-х октетов (русские буквы представлены двумя октетами). Символ в бинарном представлии будет иметь вид: 110xxxxx 10xxxxxx. В HEX представлении: [0xC0-0x6F] [0x80-0xBF]. В ISO-8859-1 этими символами едва ли можно закодировать что-то, несущее смысловую нагрузку. Поэтому риск того, что браузер неправильно расшифрует сообщение, очень мал.

Однако, при попытке использовать этот способ можно столкнуться с техническими проблемами: ваш веб-сервер или фреймворк может просто не разрешить записывать UTF-8 символы в значение заголовка. Например, Apache Tomcat вместо всех UTF-8 символов проставляет 0x3F (вопросительный знак). Разумеется, это ограничение можно обойти, но, если само приложение бьет по рукам и не дает что-то сделать, то, возможно, вам и не нужно это делать.

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

Транслит

Я думаю, что использовать транслит — eto bolee horoshee reshenie. Многие крупные популярные русские ресурсы не брезгуют использовать транслит в названиях файлов. Это гарантированное решение, которое не сломается с выпуском новых браузеров и которое не надо тестировать отдельно на каждой платформе. Хотя, разумеется, надо подумать, как преобразовывать весь спектр возможных символов, что может быть не совсем тривиально. Например, если приложение рассчитано на российскую аудиторию, то в имя файла могут попасть татарские буквы ә и ң, которые надо как-то обработать, а не просто заменять на «?».

RFC 2047

Как я уже упомянул, томкат не позволил мне проставить UTF-8 в заголовке сообщения. Отражена ли эта особенность поведения в Java docs для сервлетов? Да, отражена:

Упоминается RFC 2047. Я пробовал кодировать сообщения, используя этот формат, — браузер меня не понял. Этот метод кодировки не работает в HTTP. Хотя работал раньше. Вот, например, тикет на удаление поддержки этой кодировки из Firefox.

RFC 6266

В тикете, ссылка на который содержится в предыдущем разделе, есть упоминания, что даже после прекращения поддержки RFC 2047, все еще есть способ передавать UTF-8 значения в названии скачиваемых файлов: RFC 6266. На мой взгляд, это самое правильно решение на сегодняшний день. Многие популярные интернет ресурсы используют его. Мы в CUBA Platform также используем именно этот RFC для генерации «Content-Disposition».

RFC 6266 — это спецификация, описывающая использование “Content-Disposition” заголовка. Сам способ кодировки подробно описан в другой спецификации — RFC 8187.

Параметр “filename” содержит название файла в ASCII, “filename*” — в любой необходимой кодировке. При наличии обоих атрибутов “filename” игнорируется во всех современных браузерах (включая IE11 и старые версии Safari). Совсем старые браузеры, напротив, игнорируют “filename*”.

При использовании данного способа кодирования в параметре сначала указывается кодировка, после » идет закодированное значение. Видимые символы из ASCII кодирования не требуют. Остальные символы просто пишутся в hex представлении, со стоящим «%» перед каждым октетом.

Что делать с другими заголовками?

Кодирование, описанное в RFC 8187, не является универсальным. Да, можно поместить в заголовок параметр с * префиксом, и это, возможно, будет даже работать для некоторых браузеров, но спецификация предписывает не делать так.

В каждом случае, где в заголовках поддерживается UTF-8, на настоящий момент есть явное упоминание об этом в релевантном RFC. Помимо «Content-Disposition» данная кодировка используется, например, в Web Linking и Digest Access Authentication.

Следует учесть, что стандарты в этой области постоянно меняются. Использование описанной выше кодировки в HTTP было предложено лишь в 2010. Использование данной кодировки именно в «Content-Disposition» было зафиксировано в стандарте в 2011. Несмотря на то, что эти стандарты находятся лишь на стадии «Proposed Standard», они поддержаны повсеместно. Вариант, что в будущем нас ожидают новые стандарты, которые позволят более унифицировано работать с различными кодировками в заголовках, не исключен. Поэтому остается только следить за новостями в мире стандартов HTTP и уровня их поддержки на стороне браузеров.

Xml version = «1.0» encoding = «utf-8»?>

Чтобы понять атрибут «кодировка», вы должны понимать разницу между байтами и символами .

Думайте о байтах как о числах от 0 до 255, тогда как символы — это такие вещи, как «a», «1» и «Ä». Набор всех доступных символов называется набором символов .

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

Большинство кодировок основаны на старом наборе символов и кодировке, называемой ASCII, которая представляет собой один байт на символ (на самом деле, всего 7 бит) и содержит 128 символов, включая множество общих символов, используемых в английском языке США.

Например, вот 6 символов в наборе символов ASCII, которые представлены значениями от 60 до 65.

Extract of ASCII Table 60-65
╔══════╦══════════════╗
║ Byte ║  Character   ║
╠══════╬══════════════║
║  60  ║      <       ║
║  61  ║      =       ║
║  62  ║      >       ║
║  63  ║      ?       ║
║  64  ║      @       ║
║  65  ║      A       ║
╚══════╩══════════════╝

В полном наборе ASCII наименьшее используемое значение равно нулю, а наибольшее — 127 (оба являются скрытыми управляющими символами).

Однако, если вам нужно больше символов, чем предоставляет базовый ASCII (например, буквы с надстрочными знаками, символы валют, графические символы и т. Д.), ASCII не подходит, и вам нужно что-то более обширное. Вам нужно больше символов (другой набор символов) и другая кодировка, поскольку 128 символов недостаточно для размещения всех символов. Некоторые кодировки предлагают один байт (256 символов) или до шести байтов.

Со временем было создано множество кодировок. В мире Windows существует CP1252 или ISO-8859-1, тогда как пользователи Linux предпочитают UTF-8. Java изначально использует UTF-16.

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

Например, в ISO 8859-1 , â представлен одним байта значения 226, тогда как в UTF-8 это два байт: 195, 162. Однако, в ISO 8859-1 , 195, 162будет два символа, а, ¢ .

Представьте XML не как последовательность символов, а как последовательность байтов.

Представьте, что система, получающая XML, видит байты 195, 162. Как он узнает, что это за персонажи?

Чтобы система могла интерпретировать эти байты как фактические символы (и таким образом отображать их или преобразовывать в другую кодировку), ей необходимо знать кодировку, используемую в XML.

Поскольку наиболее распространенные кодировки совместимы с ASCII, что касается основных буквенных символов и символов, в этих случаях само объявление может уйти с использованием только символов ASCII, чтобы сказать, что такое кодировка. В других случаях парсер должен попытаться выяснить кодировку объявления. Поскольку он знает, что объявление начинается с, <?xmlэто сделать намного проще.

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

Кодирование и декодирование / Хабр

Причиной разобраться в том, как же работает UTF-8 и что такое Юникод заставил тот факт, что VBScript не имеет встроенных функций работы с UTF-8. А так как ничего рабочего не нашел, то пришлось писть/дописывать самому. Опыт на мой взгляд полезный в любом случае. Для лучшего понимания начну с теории.

О Юникоде

До появления Юникода широко использовались 8-битные кодировки, главные минусы которых очевидны:


  • Всего 255 символов, да и то часть из них не графические;
  • Возможность открыть документ не с той кодировкой, в которой он был создан;
  • Шрифты необходимо создавать для каждой кодировки.

Так и было решено создать единый стандарт «широкой» кодировки, которая включала бы все символы (при чем сначала хотели в нее включить только обычные символы, но потом передумали и начали добавлять и экзотические). Юникод использует 1 112 064 кодовых позиций (больше чем 16 бит). Начало дублирует ASCII, а дальше остаток латиницы, кирилица, другие европейские и азиатские символы. Для обозначений символов используют шестнадцатеричную запись вида «U+xxxx» для первых 65k и с большим количеством цифр для остальных.

О UTF-8

Когда-то я думал что есть Юникод, а есть UTF-8. Позже я узнал, что ошибался.

UTF-8 является лишь представлением Юникода в 8-битном виде. Символы с кодами меньше 128 представляются одним байтом, а так как в Юникоде они повторяют ASCII, то текст написанный только этими символами будет являться текстом в ASCII. Символы же с кодами от 128 кодируются 2-мя байтами, с кодами от 2048 — 3-мя, от 65536 — 4-мя. Так можно было бы и до 6-ти байт дойти, но кодировать ими уже ничего.

0x00000000 — 0x0000007F: 0xxxxxxx
0x00000080 — 0x000007FF: 110xxxxx 10xxxxxx
0x00000800 — 0x0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
0x00010000 — 0x001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

Кодируем в UTF-8

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


  • Каждый символ превращаем в Юникод.
  • Проверяем из какого символ диапазона.
  • Если код символа меньше 128, то к результату добавляем его в неизменном виде.
  • Если код символа меньше 2048, то берем последние 6 бит и первые 5 бит кода символа. К первым 5 битам добавляем 0xC0 и получаем первый байт последовательности, а к последним 6 битам добавляем 0x80 и получаем второй байт. Конкатенируем и добавляем к результату.
  • Похожим образом можем продолжить и для больших кодов, но если символ за пределами U+FFFF придется иметь дело с UTF-16 суррогатами.
Function EncodeUTF8(s)
    Dim i, c, utfc, b1, b2, b3
    
    For i=1 to Len(s)
        c = ToLong(AscW(Mid(s,i,1)))
 
        If c < 128 Then
            utfc = chr( c)
        ElseIf c < 2048 Then
            b1 = c Mod &h50
            b2 = (c - b1) / &h50
            utfc = chr(&hC0 + b2) & chr(&h80 + b1)
        ElseIf c < 65536 And (c < 55296 Or c > 57343) Then
            b1 = c Mod &h50
            b2 = ((c - b1) / &h50) Mod &h50
            b3 = (c - b1 - (&h50 * b2)) / &h2000
            utfc = chr(&hE0 + b3) & chr(&h80 + b2) & chr(&h80 + b1)
        Else
            ' Младший или старший суррогат UTF-16
            utfc = Chr(&hEF) & Chr(&hBF) & Chr(&hBD)
        End If

        EncodeUTF8 = EncodeUTF8 + utfc
    Next
End Function

Function ToLong(intVal)
    If intVal < 0 Then
        ToLong = CLng(intVal) + &h20000
    Else
        ToLong = CLng(intVal)
    End If
End Function

Декодируем UTF-8


  • Ищем первый символ вида 11xxxxxx
  • Считаем все последующие байты вида 10xxxxxx
  • Если последовательность из двух байт и первый байт вида 110xxxxx, то отсекаем приставки и складываем, умножив первый байт на 0x40.
  • Аналогично для более длинных последовательностей.
  • Заменяем всю последовательность на нужный символ Юникода.
Function DecodeUTF8(s)
    Dim i, c, n, b1, b2, b3

    i = 1
    Do While i <= len(s)
        c = asc(mid(s,i,1))
        If (c and &hC0) = &hC0 Then
            n = 1
            Do While i + n <= len(s)
                If (asc(mid(s,i+n,1)) and &hC0) <> &h80 Then
                    Exit Do
                End If
                n = n + 1
            Loop
            If n = 2 and ((c and &hE0) = &hC0) Then
                b1 = asc(mid(s,i+1,1)) and &h4F
                b2 = c and &h2F
                c = b1 + b2 * &h50
            Elseif n = 3 and ((c and &hF0) = &hE0) Then
                b1 = asc(mid(s,i+2,1)) and &h4F
                b2 = asc(mid(s,i+1,1)) and &h4F
                b3 = c and &h0F
                c = b3 * &h2000 + b2 * &h50 + b1
            Else
                ' Символ больше U+FFFF или неправильная последовательность
                c = &hFFFD
            End if
            s = left(s,i-1) + chrw( c) + mid(s,i+n)
        Elseif (c and &hC0) = &h80 then
            ' Неожидаемый продолжающий байт
            s = left(s,i-1) + chrw(&hFFFD) + mid(s,i+1)
        End If
        i = i + 1
    Loop
    DecodeUTF8 = s 
End Function

Ссылки


Юникод на Википедии
Исходник для ASP+VBScript

UPD: Обработка ошибочных последовательностей и ошибка с типом Integer, который возвращает AscW.

Общее представление о Unicode, UTF-8, UTF-16 LE/BE, BOM | by Yulya Kozlova

Существуют следующие байтовые представления символов юникода UTF-8, UTF-16(UTF-16 LE или UTF-16 BE), UTF-32(UTF-32 LE или UTF-32 BE).

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

Например, кодировка UTF-8 использует от 1 до 4 байтов и позволяет покрыть все 1,114,112 символов юникода (потенциально даже больше символов). Первые 128 символов в таблице юникод совпадают с 128 символами из кодовой таблицы ASCII, и UTF-8 кодирует эти 128 символов так же, как и ASCII. В качестве байтового представления используется просто порядковый номер в таблице.

Ниже кусок юникод-таблицы, первые 128 символов, в ней представлены порядковый номер и символ

чтобы получить порядковый номер(U+xxxx) в таблице нужно к числу строки(0000, 0010, 0020, …, 0070) добавить номер столбца(0, 1, 2, …, F), например ‘K’ — это ‘U+004B’

порядковый номер в общем случае не является машинным представлением, но в случае первых 128 символов совпадает с байтовым кодом по версии ASCII и UTF-8

ascii кодировка

тут число не просто порядковый номер, а байтовое представление символа в памяти компьютера

utf-8 кодировка

кусок таблицы, всю можно посмотреть тут

Один символ кодировки UTF-16 представлен последовательностью двух байтов или двух пар байтов (т.е. 2 или 4 байта на символ). Получается, наименьшей единицей представления символа является 2 байта (называют “слово”). Который из двух байтов в слове идёт впереди, старший или младший, зависит от порядка байтов. Суффиксы BE и LE в названии кодировки указывают порядок. BE — big-endian — от старшего к младшему; LE — little-endian — от младшего к старшему.

Чтобы определить порядок байтов на основании самого файла, используется специальный юникод-символ U+FEFF, называемый BOM (Byte Order Mark), меткой последовательности байтов. Символ U+FEFF добавляется в начало текстового файла, считав который можно определить кодировку файла.

UTF-8 Кодировка


Сводка

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

UTF означает формат преобразования Unicode. ‘8’ означает, что он использует 8-битные блоки для представляют собой персонажа. Количество блоков, необходимых для представления персонажа, варьируется от От 1 до 4.

Одной из действительно хороших особенностей UTF-8 является то, что он совместим со строками с завершающим нулем.При кодировании ни один символ не будет иметь нулевой (0) байт. Это означает, что код C, имеющий дело с char [] будет «просто работать».

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

Если вы разработчик приложений, эта статья Joel On Software о Unicode — довольно хорошее резюме всего, что вам нужно знать.

Дополнительные ссылки:


Деталь

Для любого символа, равного или меньше 127 (шестнадцатеричный 0x7F), представление UTF-8 это один байт.Это всего лишь младшие 7 бит полного значения Unicode. Это также то же самое, что и значение ASCII.

Для символов, равных или меньше 2047 (шестнадцатеричное 0x07FF), представление UTF-8 распространяется на два байта. В первом байте будут установлены два старших бита и третий бит очищен (т.е. от 0xC2 до 0xDF). Второй байт будет иметь установлен верхний бит, а второй бит очищен (то есть от 0x80 до 0xBF).

Для всех символов, равных или больше 2048, но меньше 65535 (0xFFFF), представление UTF-8 распространяется на три байта.

В следующей таблице показан формат таких байтовых последовательностей UTF-8 (где «свободные биты», обозначенные в таблице значками x, объединяются в порядок показан и интерпретируется от наиболее значимого до наименее значимого).

Двоичный формат байтов в последовательности

1-й байт 2-й байт 3-й байт 4-й байт Количество свободных битов Максимальное выражаемое значение Unicode
0xxxxxxx 7 007F шестигранник (127)
110xxxxx 10xxxxxx (5 + 6) = 11 07FF шестигранник (2047)
1110xxxx 10xxxxxx 10xxxxxx (4 + 6 + 6) = 16 FFFF шестигранник (65535)
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx (3 + 6 + 6 + 6) = 21 10FFFF шестигранник (1,114,111)

Значение каждого отдельного байта указывает его функцию UTF-8, как показано ниже:

  • 00–7F шестнадцатеричный (от 0 до 127): первый и единственный байт последовательности.
  • от 80 до BF шестнадцатеричный (от 128 до 191): продолжающий байт в многобайтовой последовательности.
  • C2 в шестнадцатеричный формат DF (от 194 до 223): первый байт двухбайтовой последовательности.
  • E0 в шестнадцатеричном формате EF (от 224 до 239): первый байт трехбайтовой последовательности.
  • F0 в шестнадцатеричном формате FF (от 240 до 255): первый байт четырехбайтовой последовательности.

UTF-8 остается простым однобайтовым, совместимым с ASCII методом кодирования до тех пор, пока непосредственно присутствуют символы больше 127. Это означает, что документ HTML технически объявленный как закодированный как UTF-8, может оставаться обычным однобайтовым файлом ASCII.Документ может остаться поэтому, даже если он может содержать символы Unicode выше 127, при условии, что все символы выше 127 упоминаются косвенно с помощью амперсандных сущностей.

Примеры закодированных символов Unicode (в шестнадцатеричной системе счисления)

16-битный Unicode Последовательность UTF-8
0001 01
007F 7F
0080 C2 80
07FF DF BF
0800 E0 A0 80
FFFF EF BF BF
010000 F0 90 80 80
10FFFF F4 8F BF BF

Введение в кодировку символов, Unicode и UTF-8 в Ruby

Очень вероятно, что вы видели исключение Ruby, например UndefinedConversionError или IncompatibleCharacterEncodings .Маловероятно, что вы поняли, что означает исключение. Эта статья поможет. Вы узнаете, как работают кодировки символов и как они реализованы в Ruby. К концу вы сможете гораздо легче понять и исправить эти ошибки.

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

На каждом языке программирования вы работаете со строками. Иногда вы обрабатываете их как входные, иногда вы отображаете их как выходные. Но ваш компьютер не понимает «строк». Он понимает только биты: единицы и нули.Процесс преобразования строк в биты называется кодировкой символов.

Но кодировка символов принадлежит не только эпохе компьютеров. До появления компьютеров мы можем учиться на более простом процессе: азбуке Морзе.

Код Морзе

Азбука Морзе очень проста в своем определении. У вас есть два символа или два способа подачи сигнала (короткий и длинный). Эти два символа представляют собой простой английский алфавит. Например:

  • A — .- (одна короткая метка и одна длинная метка)
  • E есть.(одна короткая отметка)
  • O — — (три длинных знака)

Эта система была изобретена примерно в 1837 году и позволяла кодировать весь алфавит всего двумя символами или сигналами.

Здесь вы можете поиграть с одним переводчиком онлайн.

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

От ручного к автоматическому кодированию

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

Подобно азбуке Морзе, компьютеры используют только два «символа»: 1 и 0. Вы можете сохранить в компьютере только их последовательность, и когда они будут прочитаны, их нужно интерпретировать таким образом, чтобы это было понятно пользователю. .

В обоих случаях процесс работает следующим образом:

  Сообщение -> Кодирование -> Сохранить / Отправить -> Декодирование -> Сообщение
  

SOS азбукой Морзе это будет:

  SOS -> Кодировать ('SOS') -> ... --- ... -> Декодировать ('... --- ... ') -> SOS
----------------------- --------------------------
       Отправитель Получатель
  

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

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

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

ASCII (1963)

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

ASCII работал путем связывания каждого символа с десятичным числом, которое можно было преобразовать в двоичный код.Давайте посмотрим на пример:

«A» — это 65 в ASCII, поэтому нам нужно перевести 65 в двоичный код.

Если вы не знаете, как это работает, вот быстрый способ : Мы начинаем делить 65 на 2 и продолжаем, пока не получим 0. Если деление неточно, мы добавляем 1 в качестве остатка:

  65/2 = 32 + 1
32/2 = 16 + 0
16/2 = 8 + 0
8/2 = 4 + 0
4/2 = 2 + 0
2/2 = 1 + 0
1/2 = 0 + 1
  

Теперь берем остатки и складываем их в обратном порядке:

Таким образом, мы сохраним «A» как «1000001» с исходной кодировкой ASCII, теперь известной как US-ASCII.7 символов = 127.

Вот полная таблица:

(С http://www.plcdev.com/ascii_chart)

Проблема с ASCII

Что бы произошло, если бы мы захотели добавить еще один символ, например французский ç или японский иероглиф 大?

Да, у нас будет проблема.

После ASCII люди пытались решить эту проблему, создав свои собственные системы кодирования. Они использовали больше бит, но это в конечном итоге вызвало другую проблему.

Основная проблема заключалась в том, что при чтении файла вы не знали, есть ли у вас определенная система кодирования.Попытка интерпретировать его с помощью неправильной кодировки привела к тарабарщине типа «���» или «Ã, ÂÃ⠀ šÃ‚».

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

После многих лет борьбы с этим был создан новый стандарт: Unicode. Этот стандарт определил способ кодирования и декодирования информации современными компьютерами.

Юникод (1988)

Цель

Unicode очень проста. Согласно его официальному сайту: «Чтобы предоставить уникальный номер для каждого символа, независимо от платформы, программы или языка».

Таким образом, каждому символу в языке назначен уникальный код, также известный как кодовая точка. В настоящее время насчитывается более 137 000 символов.

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

Те же люди, которые создали язык программирования Go, Роб Пайк и Кен Томпсон, также создали UTF-8. Он добился успеха, потому что он эффективно и умно кодирует эти числа. Посмотрим, почему именно.

UTF-8: формат преобразования Unicode (1993)

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

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

Есть два очень важных аспекта UTF-8: — Эффективен при хранении битов, так как символ может занимать от 1 до 4 байтов. — Благодаря использованию Unicode и динамического количества байтов он совместим с кодировкой ASCII, поскольку первые 127 символов занимают 1 байт. Это означает, что вы можете открыть файл ASCII как UTF-8.

Давайте разберемся, как работает UTF-8.

UTF-8 с 1 байтом

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

Для первых 127 используется следующий шаблон: Ржавчина1 0_______

Таким образом, всегда будет 0, за которым следует двоичное число, представляющее значение в Юникоде (которое также будет ASCII). Например: A = 65 = 1000001.

Давайте проверим это с помощью Ruby, используя метод unpack в String:

  'A'.unpack (' B * '). First

# 01000001
  

Буква B означает, что нам нужен двоичный формат со старшим битом первым.В данном контексте это означает бит с наивысшим значением. Звездочка говорит Ruby продолжать, пока не кончатся биты. Если бы мы использовали вместо этого число, мы получили бы биты только до этого числа:

  'A'. Распаковка ('B4'). Первая

# 01000
  

UTF-8 с 2 байтами

Если у нас есть символ, значение или кодовая точка которого в Unicode превышает 127, до 2047, мы используем два байта со следующим шаблоном:

Итак, у нас есть 11 пустых битов для значения в Юникоде.Давайте посмотрим на пример:

À — это 192 в Юникоде, поэтому в двоичном формате это 11000000, что занимает 8 бит. Он не помещается в первый шаблон, поэтому мы используем второй:

Начинаем заполнять поля справа налево:

Что там происходит с пустыми битами? Мы просто ставим нули, поэтому окончательный результат будет: 11000011 10000000.

Здесь мы можем увидеть закономерность. Если мы начнем чтение слева направо, первая группа из 8 бит будет иметь две единицы в начале. Это означает, что символ займет 2 байта:

Опять же, мы можем проверить это с помощью Ruby:

  «А».распаковать ('B *'). сначала

# 1100001110000000
  

Небольшой совет: мы можем лучше отформатировать вывод с помощью:

  'À'.unpack (' B8 B8 '). Join (' ')

# 11000011 10000000
  

Мы получаем массив из 'À'.unpack (' B8 B8 ') , а затем соединяем элементы пробелом, чтобы получить строку. Восьмерки в параметре unpack говорят Ruby получить 8 бит в 2 группы.

UTF-8 с 3 байтами

Если значение в Unicode для символа не соответствует 11 битам, доступным в предыдущем шаблоне, нам понадобится дополнительный байт:

  1110____ 10______ 10______
  

Опять же, три единицы в начале шаблона говорят нам, что мы собираемся прочитать 3-байтовый символ.

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

UTF-8 с 4 байтами

Некоторые значения занимают даже больше, чем 11 пустых битов, которые были в предыдущем шаблоне. Давайте посмотрим на пример с эмодзи 🙂, который для Юникода также можно рассматривать как такой символ, как «a» или «大».

Значение или кодовая точка «» в Юникоде — 128578.Это число в двоичном формате: 11111011001000010, 17 бит. Это означает, что он не помещается в 3-байтовый шаблон, поскольку у нас было только 16 пустых слотов, поэтому нам нужно использовать новый шаблон, который занимает 4 байта в памяти:

  11110___ 10______ 10______ 10______
  

Начнем снова, заполнив его числом в двоичном формате: Ржавчина1 11110___ 10_11111 10011001 10000010

А теперь заполняем остальные нулями: Ржавчина1 1111000 10011111 10011001 10000010

Посмотрим, как это выглядит в Ruby.

Поскольку мы уже знаем, что это займет 4 байта, мы можем оптимизировать для лучшей читаемости вывода:

  '🙂'.unpack (' B8 B8 B8 B8 '). Join (' ')

# 11110000 10011111 10011001 10000010
  

Но если бы мы этого не сделали, мы могли бы просто использовать:

Мы также можем использовать строковый метод «bytes» для извлечения байтов в массив:

  🙂. Байтов

# [240, 159, 153, 130]
  

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

  "🙂".bytes.map {| e | e.to_s 2}

# ["11110000", "10011111", "10011001", "10000010"]
  

И если бы нам нужна была строка, мы могли бы использовать join:

  "🙂" .bytes.map {| e | e.to_s 2} .join ('')

# 11110000 10011111 10011001 10000010
  

UTF-8 имеет больше места, чем необходимо для Unicode

Еще одним важным аспектом UTF-8 является то, что он может включать все значения Unicode (или кодовые точки) — и не только те, которые существуют сегодня, но также и те, которые будут существовать в будущем.21 (= 2 097 152) значения, намного больше, чем наибольшее количество значений Unicode, которое мы когда-либо будем иметь со стандартом, около 1,1 миллиона.

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

Работа с разными кодировками в Ruby

В Ruby мы можем сразу увидеть кодировку данной строки, сделав это:

  'Hello'.encoding.name

# "UTF-8"
  

Мы также можем закодировать строку с другой системой кодирования.Например:

  encoded_string = 'привет, как дела?'. Encode ("ISO-8859-1", "UTF-8")

encoded_string.encoding.name

# ISO-8859-1
  

Если преобразование несовместимо, по умолчанию выдается ошибка. Допустим, мы хотим преобразовать «hello 🙂» из UTF-8 в ASCII. Поскольку смайлик «🙂» не подходит для ASCII, мы не можем. В этом случае Ruby выдает ошибку:

  "привет 🙂" .encode ("ASCII", "UTF-8")

# Encoding :: UndefinedConversionError (U + 1F642 из UTF-8 в US-ASCII)
  

Но Ruby позволяет нам иметь исключения, когда, если символ не может быть закодирован, мы можем заменить его на «?».

  "привет 🙂" .encode ("ASCII", "UTF-8", undef:: replace)

# Привет ?
  

У нас также есть возможность заменить определенные символы допустимым символом в новой кодировке:

  "hello 🙂" .encode ("ASCII", "UTF-8", резерв: {"🙂" => ":)"})

# Привет :)
  

Проверка кодировки сценария для сценария в Ruby

Чтобы увидеть кодировку файла сценария, над которым вы работаете, файла «.rb», вы можете сделать следующее:

  __ENCODING__

# В моем случае будет отображаться "# ". 

Начиная с Ruby 2.0, кодировка по умолчанию для скриптов Ruby — UTF-8, но вы можете изменить это с помощью комментария в первой строке:

  # кодировка: ASCII

__ENCODING__
# # <Кодировка: US-ASCII>
  

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

Несколько советов по работе с кодировками в Ruby

Вы можете увидеть весь список поддерживаемых кодировок в Ruby с кодировкой .список_именов . Это вернет большой массив:

  [«ASCII-8BIT», «UTF-8», «US-ASCII», «UTF-16BE», «UTF-16LE», «UTF-32BE», «UTF-32LE», «UTF-16» , «UTF-32», «UTF8-MAC» ...
  

Другой важный аспект при работе с символами вне английского языка заключается в том, что до Ruby 2.4 некоторые методы, такие как upcase или reverse , не работали должным образом. Например, в Ruby 2.3 upcase работает не так, как вы думаете:

  # Рубин 2.3
'öıüëâñà'.upcase

# 'öıüëâñà'
  

Обходной путь использовал ActiveSupport из Rails или другой внешний гем, но начиная с Ruby 2.4 у нас есть полное отображение регистра Unicode:

  # Начиная с Ruby 2.4 и выше
'öıüëâñà'.upcase

# 'ÖIÜËÂÑÀ'
  

Немного повеселитесь с смайликами

Давайте посмотрим, как смайлики работают в Unicode и Ruby:

Это «Поднятая рука с частью между средним и безымянным пальцами», также известная как смайлик «Вулканский салют». Если у нас есть тот же смайлик, но с другим оттенком кожи, который не используется по умолчанию, происходит кое-что интересное:

  '🖖🏾'.символы

# ["🖖", "🏾"]
  

Итак, вместо одного персонажа у нас есть два для одного смайлика.

Что там произошло?

Ну, некоторые символы в Юникоде определяются как комбинация нескольких символов. В этом случае, если компьютер видит эти два символа вместе, он показывает только один с примененным оттенком кожи.

Есть еще один забавный пример с флагами.

  '🇦🇺'.chars

# ["🇦", "🇺"]
  

В Юникоде эмодзи флага внутренне представлены некоторыми абстрактными символами Юникода, называемыми «региональными символами индикатора», такими как 🇦 или 🇿.Обычно они не используются вне флагов, и когда компьютер видит два символа вместе, он показывает флаг, если он есть для этой комбинации.

Чтобы убедиться в этом, попробуйте скопировать это и удалить запятую в любом текстовом редакторе или поле:

Заключение

Я надеюсь, что этот обзор того, как работают Unicode и UTF-8 и как они соотносятся с Ruby и потенциальными ошибками, был вам полезен.

Самый важный урок, который нужно усвоить, — это помнить, что при работе с любым текстом у вас есть связанная система кодирования, и очень важно сохранить ее при сохранении или изменении.По возможности используйте современную систему кодирования, такую ​​как UTF-8, чтобы не менять ее в будущем.

Примечание о выпусках Ruby

Я использовал Ruby 2.6.5 для всех примеров в этой статье. Вы можете попробовать их в онлайн-REPL или локально, перейдя в свой терминал и выполнив irb , если у вас установлен Ruby.

Поскольку в последних выпусках поддержка Unicode была улучшена, я решил использовать последнюю версию, поэтому эта статья останется актуальной. Во всяком случае, с Ruby 2.4 и выше, все примеры должны работать, как показано здесь.

Кодировка

— что такое Unicode, UTF-8, UTF-16?

Зачем нам Юникод?

В (не слишком) ранние дни все, что существовало, было ASCII. Это было нормально, поскольку все, что когда-либо понадобилось, — это несколько управляющих символов, знаков препинания, цифр и букв, подобных тем, что в этом предложении. К сожалению, сегодняшний странный мир глобального взаимодействия и социальных сетей не был предвиден, и нет ничего необычного в том, чтобы увидеть в одном документе английский, العربية, 汉语, עִבְרִית, ελληνικ the и ភាសាខ្មែរ браузеры).

Но ради аргументации, допустим, Джо Средний — разработчик программного обеспечения. Он настаивает на том, что ему когда-либо понадобится только английский, и поэтому он хочет использовать только ASCII. Это может быть хорошо для Джо, пользователя , , но не для Джо, разработчика программного обеспечения , . Примерно половина мира использует нелатинские символы, и использование ASCII, возможно, невнимательно для этих людей, и, кроме того, он закрывает свое программное обеспечение для большой и растущей экономики.

Следовательно, необходим всеобъемлющий набор символов, включающий всех языков.Так появился Unicode. Он присваивает каждому символу уникальный номер, называемый кодовой точкой . Одним из преимуществ Unicode перед другими возможными наборами является то, что первые 256 кодовых точек идентичны ISO-8859-1 и, следовательно, также ASCII. Кроме того, подавляющее большинство обычно используемых символов представляются всего двумя байтами в области, называемой Basic Multilingual Plane (BMP) . Теперь для доступа к этому набору символов необходима кодировка символов, и в ответ на вопрос, я сосредоточусь на UTF-8 и UTF-16.

Рекомендации по использованию памяти

Так сколько байтов к каким символам дают доступ в этих кодировках?

  • UTF-8:
    • 1 байт: стандартный ASCII
    • 2 байта: арабский, иврит, большинство европейских алфавитов (в первую очередь за исключением грузинского)
    • 3 байта: BMP
    • 4 байта: все символы Unicode
  • UTF-16:
    • 2 байта: BMP
    • 4 байта: все символы Unicode

Теперь стоит упомянуть, что символы, отсутствующие в BMP, включают древние шрифты, математические символы, музыкальные символы и более редкие символы китайского / японского / корейского (CJK).

Если вы будете работать в основном с символами ASCII, то UTF-8, безусловно, более эффективен с точки зрения памяти. Однако, если вы работаете в основном с неевропейскими скриптами, использование UTF-8 может быть в 1,5 раза менее эффективным с точки зрения памяти, чем UTF-16. При работе с большими объемами текста, такими как большие веб-страницы или длинные текстовые документы, это может повлиять на производительность.

Основы кодирования

Примечание. Если вы знаете, как кодируются UTF-8 и UTF-16, перейдите к следующему разделу, посвященному практическому применению.

  • UTF-8: Для стандартных символов ASCII (0–127) коды UTF-8 идентичны. Это делает UTF-8 идеальным, если требуется обратная совместимость с существующим текстом ASCII. Для других символов требуется от 2 до 4 байтов. Это делается путем резервирования некоторых битов в каждом из этих байтов, чтобы указать, что он является частью многобайтового символа. В частности, первый бит каждого байта — 1 , чтобы избежать конфликтов с символами ASCII.
  • UTF-16: Для допустимых символов BMP представление UTF-16 — это просто его кодовая точка.Однако для символов, отличных от BMP, UTF-16 вводит суррогатных пар . В этом случае комбинация двух двухбайтовых частей отображается на символ, отличный от BMP. Эти двухбайтовые части берутся из числового диапазона BMP, но стандарт Unicode гарантирует их недопустимость в качестве символов BMP. Кроме того, поскольку в качестве основной единицы UTF-16 имеет два байта, на него влияет порядок байтов. Для компенсации зарезервированная метка порядка байтов может быть помещена в начало потока данных, что указывает на порядок байтов.Таким образом, если вы читаете ввод UTF-16, а порядок байтов не указан, вы должны это проверить.

Как видно, UTF-8 и UTF-16 далеко не совместимы друг с другом. Поэтому, если вы выполняете ввод-вывод, убедитесь, что вы знаете, какую кодировку вы используете! Дополнительные сведения об этих кодировках см. В разделе часто задаваемых вопросов по UTF.

Практические рекомендации по программированию

Символьные и строковые типы данных: Как они закодированы в языке программирования? Если это необработанные байты, в ту минуту, когда вы попытаетесь вывести символы, отличные от ASCII, вы можете столкнуться с несколькими проблемами.Кроме того, даже если тип символа основан на UTF, это не означает, что строки являются правильным UTF. Они могут допускать недопустимые последовательности байтов. Как правило, вам придется использовать библиотеку, поддерживающую UTF, например ICU для C, C ++ и Java. В любом случае, если вы хотите вводить / выводить что-то, кроме кодировки по умолчанию, вам сначала нужно будет ее преобразовать.

Рекомендуемые / стандартные / доминирующие кодировки: Когда есть выбор, какой UTF использовать, обычно лучше следовать рекомендованным стандартам для среды, в которой вы работаете.Например, UTF-8 является доминирующим в Интернете, а начиная с HTML5, это рекомендуемая кодировка. И наоборот, как среды .NET, так и Java основаны на символьном типе UTF-16. Как это ни сбивает с толку (и неправильно), часто делаются ссылки на «кодировку Unicode», которая обычно относится к доминирующей кодировке UTF в данной среде.

Поддержка библиотек: Используемые библиотеки поддерживают некоторую кодировку. Который из? Поддерживают ли они угловые случаи? Поскольку необходимость — мать изобретения, библиотеки UTF-8, как правило, будут правильно поддерживать 4-байтовые символы, поскольку 1, 2 и даже 3-байтовые символы могут встречаться часто.Однако не все предполагаемые библиотеки UTF-16 поддерживают суррогатные пары должным образом, поскольку они встречаются очень редко.

Подсчет символов: Существует , объединяющих символов в Unicode. Например, кодовая точка U + 006E (n) и U + 0303 (объединяющая тильда) образует ñ, а кодовая точка U + 00F1 образует ñ. Они должны выглядеть одинаково, но простой алгоритм подсчета вернет 2 в первом примере и 1 во втором. Это не обязательно неправильно, но может и не быть желаемым результатом.

Сравнение на равенство: A, А и Α выглядят одинаково, но они латынь, кириллица и греческий соответственно. У вас также есть падежи, такие как C и Ⅽ, одна буква, другая римская цифра. Кроме того, нам нужно учитывать и комбинирующие символы. Для получения дополнительной информации см. Дублирующиеся символы в Юникоде.

Суррогатные пары: Они достаточно часто встречаются на SO, поэтому я просто приведу несколько примеров ссылок:

Другое ?:

Кодировка

— В чем разница между UTF-8 и Unicode?

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

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

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

Старые кодировки символов, такие как ASCII, относятся к (до) 8-битной эпохе и пытаются втиснуть доминирующий язык в вычислениях того времени, т.е.е. Английский, в числах от 0 до 127 (7 бит). С 26 буквами в алфавите, как заглавными, так и не заглавными, числами и знаками препинания, это сработало очень хорошо. ASCII был расширен на 8-й бит для других, не английских языков, но дополнительные 128 цифр / кодовых точек, предоставляемые этим расширением, будут сопоставлены с разными символами в зависимости от отображаемого языка. Стандарты ISO-8859 являются наиболее распространенными формами этого сопоставления; ISO-8859-1 и ISO-8859-15 (также известные как ISO-Latin-1, latin1, и да, есть две разные версии стандарта 8859 ISO).

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

По сути, существует два разных типа кодирования: один расширяет диапазон значений, добавляя дополнительные биты. Примерами этих кодировок могут быть UCS2 (2 байта = 16 бит) и UCS4 (4 байта = 32 бита). По сути, они страдают той же проблемой, что и стандарты ASCII и ISO-8859, поскольку их диапазон значений все еще ограничен, даже если предел значительно выше.

Другой тип кодирования использует переменное количество байтов на символ, и наиболее широко известными кодировками для этого являются кодировки UTF. Все кодировки UTF работают примерно одинаково: вы выбираете размер блока, который для UTF-8 составляет 8 бит, для UTF-16 — 16 бит, а для UTF-32 — 32 бит. Затем стандарт определяет некоторые из этих битов как флаги: если они установлены, то следующая единица в последовательности единиц должна считаться частью того же символа. Если они не установлены, эта единица полностью представляет один символ.Таким образом, наиболее распространенные (английские) символы занимают только один байт в UTF-8 (два в UTF-16, 4 в UTF-32), но символы других языков могут занимать шесть байтов и более.

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

Как стандарты UCS, так и стандарты UTF кодируют кодовые точки, как определено в Unicode. Теоретически эти кодировки могут использоваться для кодирования любого числа (в пределах диапазона, поддерживаемого кодированием), но, конечно, эти кодировки были сделаны для кодирования кодовых точек Unicode. И это ваши отношения между ними.

Windows обрабатывает так называемые строки «Unicode» как строки UTF-16, в то время как большинство UNIX в наши дни по умолчанию используют UTF-8. Коммуникационные протоколы, такие как HTTP, как правило, лучше всего работают с UTF-8, поскольку размер блока в UTF-8 такой же, как и в ASCII, и большинство таких протоколов были разработаны в эпоху ASCII.С другой стороны, UTF-16 дает лучшую , среднюю производительность пространства / обработки при представлении всех живых языков.

Стандарт Unicode определяет меньше кодовых точек, чем может быть представлено в 32 битах. Таким образом, для всех практических целей UTF-32 и UCS4 стали одной и той же кодировкой, поскольку вам вряд ли придется иметь дело с многозначными символами в UTF-32.

Надеюсь, что пополнит ряд деталей.

unicode — Почему добавление двух байтов кодировки UTF-8 не дает кодовой точки символа?

UTF-8 — схема кодирования.Недостаточно просто сложить необработанные байты вместе, вы должны сначала удалить закодированные части, а затем объединить (не складывать) оставшиеся биты вместе.

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

Char. диапазон номеров | Последовательность октетов UTF-8
   (шестнадцатеричный) | (двоичный)
-------------------- + ----------------------------- ----------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
 

Это лучше визуализировано в Википедии:

Теперь давайте применим это к вашему примеру символа Unicode, é , который является кодовой точкой Unicode U + 00E9 МАЛЕНЬКАЯ БУКВА E С ОСТРЫМ .

Если вы посмотрите на таблицу, кодовая точка U + 00E9 попадает во вторую строку, поэтому она кодируется с использованием двух байтов:

  110xxxxx 10xxxxxx
  

Значения 1 и 0 являются буквальными, они должны отображаться, как показано в закодированных байтах. x s представляют собой исходные биты кодируемой точки.

Установка старшего бита на 1 гарантирует, что байты не будут перепутаны с 7-битными символами ASCII.Число 1 в первом байте закодированной последовательности также служит для определения общего количества байтов в полной последовательности.

Полный репертуар Unicode требует максимум 21 бита для представления всех возможных кодовых точек (до U + 10FFFF включительно, что является наивысшей кодовой точкой, которую UTF-16 может физически кодировать. UTF-8 может физически кодировать более высокие кодовые точки, но искусственно ограничено RFC для обеспечения 100% совместимости с UTF-16).Поскольку в большинстве языков программирования нет 21-битного типа данных, следующим по величине типом данных является 32-битное целое число.

Кодовая точка U + 00E9 — это 0x000000E9 как 32-битное число в шестнадцатеричном формате. То есть 00000000 0000000 00000000 11101001 в двоичных битах. Вторая строка таблицы использует только 11 битов из кодовой точки, поэтому вы должны убрать старший 21 бит и заполнить x с оставшимися 11 младшими битами:

  11000000 10000000
ИЛИ 00011 101001
--------------------
   11000011 10101001 = 0xC3 0xA9
  

Чтобы обратить процесс вспять, просто удалите из каждого байта биты, отличные от x , и соедините оставшиеся биты вместе:

  11000011 10101001
И 00011111 00111111
---------------------
       00011 101001 = 11101001 = 0xE9
  

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

Encoding 101 — Часть 2: Windows-1252 против UTF-8

Продолжение с Кодировка 101 часть 1

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

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

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

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

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

Окна-1252

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

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

UTF-8

UTF-8 — это кодировка из стандарта Unicode. UTF означает формат преобразования Unicode, а цифра 8 в конце означает, что это 8-битная кодировка переменной. Это означает, что каждый символ использует не менее 8 бит для своей кодовой точки, но некоторые могут использовать больше. Как и в Windows-1252, первые 128 кодовых точек идентичны ASCII, но выше эти две кодировки значительно отличаются. В то время как Windows-1252 содержит всего 256 кодовых точек, UTF-8 имеет кодовые точки для всего набора символов Unicode.Это делается путем определения некоторых байтовых значений выше 127 в качестве префиксов для дальнейших байтовых значений. Например, символ авторского права (©) кодируется как C2 A9, а знак фунта (£) кодируется как C2 A3. Поскольку байт C2 разработан как префиксный байт, это открывает дополнительные 256 2-байтовых кодовых точек с C2 в качестве первого байта.

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

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

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

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

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

utf-8

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

См. Также

Unicode и UTF-8

Внутренние компоненты Tcl

Внутри Tcl использует модифицированную кодировку utf-8, которая аналогична utf-8, за исключением того, что символ NUL (\ u0000) кодируется как байты 0xC0 0x80, что не является допустимой последовательностью utf-8. Поскольку в такой строке нет нулей, свойство C-строки, заключающееся в том, что нулевой байт завершает строку, может быть сохранено.


DKF: Вот небольшая служебная процедура, которую я написал сегодня, когда мне нужно было преобразовать кодировку utf-8 символа Unicode в последовательность шестнадцатеричных цифр для использования в качестве буквального значения в C:

 proc toutf8 c {
    установить s [кодировка преобразована в utf-8 $ c]
    двоичное сканирование $ s cu * x
    формат [повтор строки \\ x% 02x [длина строки $ s]] {*} $ x
} 

Демонстрирует:

 %  toutf8 \ u1234
  \ xe1 \ x88 \ xb4 
 %  toutf8 \ u0000
  \ x00  

ferrieux: Могу я предложить небольшое улучшение читаемости и, возможно, производительности, хотя не измеряется и не ожидает многого:

 proc toutf8 c {
    установить s [кодировка преобразована в utf-8 $ c]
    двоичное сканирование $ s H * x
    regsub -all -expanded {..} $ x {\ x &}
} 

Продемонстрированный результат остается таким же, как показано выше, как и ожидалось.


jima 2010-01-09: Работает ли это для символов Юникода в диапазоне от U + 010000 до U + 10FFFF?

U + 010000 — xF0 x90 x80 x80

Согласно [L1]

В моем ящике

Производит

И (обратите внимание на дополнительный 0, введенный здесь)

Производит

Я тестировал некоторые коды в другие диапазоны определены в [L2], и все выглядит нормально, пока мы не помещаем лишние нули в начало:

 toutf8 \ u20ac 

Правильно производит

 \ xe2 \ x82 \ xac 

Lars H, 2010-01 -12 PYK 2020-07-22: Нет, Tcl может (в настоящее время) представлять только символы в пределах базовой многоязычной плоскости юникода, поэтому вы даже не можете передать символ U + 10000 в кодировку convertto: — (.Исправить это нетривиально, поскольку некоторые части библиотеки Tcl C требуют представления строк, в которых все символы занимают одинаковое количество байтов. Можно скомпилировать Tcl с этим TCL_UTF_MAX, установленным на 4, что означает 32 бита на символ, но это довольно расточительно и, как сообщается, не полностью совместим с Tk.

Часто можно обойтись использованием суррогатных пар для символов за пределами BMP, таким образом обрабатывая строки Tcl как представления самих строк в кодировке UTF-16.Это не очень хорошо работает с кодировкой convertto utf-8, поскольку это перекодирует каждый суррогат в паре как отдельный символ. Возможно, мне стоит что-то с этим сделать…

\ u по замыслу захватывает не более четырех шестнадцатеричных цифр, оставляя лишние нули в покое, и продолжу делать это даже после того, как Tcl будет расширен для поддержки полного юникода. Это сделано для того, чтобы вы могли поставить шестнадцатеричную цифру сразу после четырехзначной замены \ u, что невозможно с \ x, которая потребляет столько шестнадцатеричных цифр, сколько находит.Возможно, будет замена на \ U для всего диапазона. regexp уже реализует это, по крайней мере, синтаксически.

jima, 13.01.2010: Спасибо, Ларс за объяснение.

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

Итак (насколько я понимаю), чтобы отобразить изображение символа Unicode U + 10000, правильно закодированного внутри как utf-8 как \ xF0 \ x90 \ x80 \ x80, нам потребуется дополнительная информация помимо алгоритма, изображенного в упомянутая ранее страница википедии.

Ларс Х, 2010-01-19, 2010-01-26: Графический вывод всегда зависит от того, что должно его поставлять. Tk, вероятно, сложен, но если вы скорее создаете текст, который должна отображать какая-либо другая программа (например, веб-браузер), тогда 4-байтовые последовательности UTF-8 могут подойти.

Как бы то ни было, я пошел дальше с идеей «суррогатные пары внутри Tcl — 4-байтовые последовательности вне»; результат пока можно найти в Half Bakery по адресу https://wiki.tcl-lang.org/_repo/UTF/.Это расширение с кодом C (ну, файлы, необходимые для одного, которые не совпадают с расширением sample), имя пакета которого — UTF, и которое определяет новую кодировку «UTF-8» (верхний регистр, тогда как встроенный — utf-8). При кодировании convertfrom он преобразует 4-байтовые последовательности (кодовые точки от U + 10000 до U + 10FFFF) в суррогатные пары, а при кодировании convertto преобразует суррогатные пары в 4-байтовые последовательности. Есть даже тесты, которые он проходит! В предыдущей версии (от 19 января 2010 г.) была ошибка, из-за которой она зависала в бесконечном цикле при использовании в качестве кодировки канала, но текущая, похоже, работает нормально.

Следующим логическим шагом было бы также реализовать UTF-16BE и UTF-16LE в качестве кодировок (встроенная кодировка Unicode — почти одна из них, но это зависит от платформы).

AMG: См. [L3] для запроса функции кодирования UTF-16.


.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *