В какой кодировке хранятся символы в строковых переменных javascript
Перейти к содержимому

В какой кодировке хранятся символы в строковых переменных javascript

  • автор:

Юникод: необходимый практический минимум для каждого разработчика

Юникод — это очень большой и сложный мир, ведь стандарт позволяет ни много ни мало представлять и работать в компьютере со всеми основными письменностями мира. Некоторые системы письма существуют уже более тысячи лет, причём многие из них развивались почти независимо друг от друга в разных уголках мира. Люди так много всего придумали и оно зачастую настолько непохоже друг на друга, что объединить всё это в единый стандарт было крайне непростой и амбициозной задачей.

Чтобы по-настоящему разобраться с Юникодом нужно хотя бы поверхностно представлять себе особенности всех письменностей, с которыми позволяет работать стандарт. Но так ли это нужно каждому разработчику? Мы скажем, что нет. Для использования Юникода в большинстве повседневных задач, достаточно владеть разумным минимумом сведений, а дальше углубляться в стандарт по мере необходимости.

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

Зачем понадобился Юникод?

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

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

У такого подхода в целом и однобайтовых кодировок в частности был ряд существенных недостатков:

  1. Можно было одновременно работать лишь с 256 символами, причём первые 128 были зарезервированы под латинские и управляющие символы, а во второй половине кроме символов национального алфавита нужно было найти место для символов псевдографики (╔ ╗).
  2. Шрифты были привязаны к конкретной кодировке.
  3. Каждая кодировка представляла свой набор символов и конвертация из одной в другую была возможна только с частичными потерями, когда отсутствующие символы заменялись на графически похожие.
  4. Перенос файлов между устройствами под управлением разных операционных систем был затруднителен. Нужно было либо иметь программу-конвертер, либо таскать вместе с файлом дополнительные шрифты. Существование Интернета каким мы его знаем было невозможным.
  5. В мире существуют неалфавитные системы письма (иероглифическая письменность), которые в однобайтной кодировке непредставимы в принципе.

Основные принципы Юникода

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

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

Вводится понятие абстрактного юникод-символа, существующего исключительно в виде умозрительной концепции и договорённости между людьми, закреплённой стандартом. Каждому юникод-символу поставлено в соответствие неотрицательное целое число, именуемое его кодовой позицией (code point).

Так, например, юникод-символ U+041F — это заглавная кириллическая буква П. Существует несколько возможностей представления данного символа в памяти компьютера, ровно как и несколько тысяч способов отображения его на экране монитора. Но при этом П, оно и в Африке будет П или U+041F.

image

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

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

Привет U+041F U+0440 U+0438 U+0432 U+0435 U+0442 

записать на листочке, упаковать в конверт и переслать в любой конец Земли. Если там знают о существовании Юникода, то текст будет воспринят ими ровно так же, как и нами с вами. У них не будет ни малейших сомнений, что предпоследний символ — это именно кириллическая строчная е (U+0435), а не скажем латинская маленькая e (U+0065). Обратите внимание, что мы ни слова не сказали о байтовом представлении.

Хотя юникод-символы и называются символами, они далеко не всегда соответствуют символу в традиционно-наивном понимании, например букве, цифре, пунктуационному знаку или иероглифу. (Подробнее смотри под спойлером.)

Примеры различных юникод-символов

  • U+0000: нулевой символ;
  • U+D800–U+DFFF: младшие и старшие суррогаты для технического представления кодовых позиций в диапазоне от 10000 до 10FFFF (читай: за пределами БМЯП/BMP) в семействе кодировок UTF-16;
  • и т.д.
  • U+0020 (пробел);
  • U+00A0 (неразрывный пробел,   в HTML);
  • U+2002 (полукруглая шпация или En Space);
  • U+2003 (круглая шпация или Em Space);
  • и т.д.
  • U+0300 и U+0301: знаки основного (острого) и второстепенного (слабого) ударений;
  • U+0306: кратка (надстрочная дуга), как в й;
  • U+0303: надстрочная тильда;
  • и т.д.

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

Кодовое пространство Юникода

Кодовое пространство Юникода состоит из 1 114 112 кодовых позиций в диапазоне от 0 до 10FFFF. Из них к девятой версии стандарта значения присвоены лишь 128 237. Часть пространства зарезервирована для частного использования и консорциум Юникода обещает никогда не присваивать значения позициям из этих специальный областей.

Ради удобства всё пространство поделено на 17 плоскостей (сейчас задействовано шесть их них). До недавнего времени было принято говорить, что скорее всего вам придётся столкнуться только с базовой многоязыковой плоскостью (Basic Multilingual Plane, BMP), включающей в себя юникод-символы от U+0000 до U+FFFF. (Забегая немного вперёд: символы из BMP представляются в UTF-16 двумя байтами, а не четырьмя). В 2016 году этот тезис уже вызывает сомнения. Так, например, популярные символы Эмодзи вполне могут встретиться в пользовательском сообщении и нужно уметь их корректно обрабатывать.

Кодировки

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

Стандарт Юникода включает в себя описание ряда юникод-кодировок, например UTF-8 и UTF-16BE/UTF-16LE, которые позволяют кодировать всё пространство кодовых позиций. Конвертация между этими кодировками может свободно осуществляться без потерь информации.

Также никто не отменял однобайтные кодировки, но они позволяют закодировать свой индивидуальный и очень узкий кусочек юникод-спектра — 256 или менее кодовых позиций. Для таких кодировок существуют и доступны всем желающим таблицы, где каждому значению единственного байта сопоставлен юникод-символ (см. например CP1251.TXT). Несмотря на ограничения, однобайтные кодировки оказываются весьма практичными, если речь идёт о работе с большим массивом моноязыковой текстовой информации.

Из юникод-кодировок самой распространённой в Интернете является UTF-8 (она завоевала пальму первенства в 2008 году), главным образом благодаря её экономичности и прозрачной совместимости с семибитной ASCII. Латинские и служебные символы, основные знаки препинания и цифры — т.е. все символы семибитной ASCII — кодируются в UTF-8 одним байтом, тем же, что и в ASCII. Символы многих основных письменностей, не считая некоторых более редких иероглифических знаков, представлены в ней двумя или тремя байтами. Самая большая из определённых стандартом кодовых позиций — 10FFFF — кодируется четырьмя байтами.

Обратите внимание, что UTF-8 — это кодировка с переменной длиной кода. Каждый юникод-символ в ней представляется последовательностью кодовых квантов с минимальной длиной в один квант. Число 8 означает битовую длину кодового кванта (code unit) — 8 бит. Для семейства кодировок UTF-16 размер кодового кванта составляет, соответственно, 16 бит. Для UTF-32 — 32 бита.

Если вы пересылаете по сети HTML-страницу с кириллическим текстом, то UTF-8 может дать весьма ощутимый выигрыш, т.к. вся разметка, а также JavaScript и CSS блоки будут эффективно кодироваться одним байтом. К примеру главная страница Хабра в UTF-8 занимает 139Кб, а в UTF-16 уже 256Кб. Для сравнения, если использовать win-1251 с потерей возможности сохранять некоторые символы, то размер, по сравнению с UTF-8, сократится всего на 11Кб до 128Кб.

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

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

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

В сухом остатке

Информации много и имеет смысл привести краткую выжимку всего, что было написано выше:

  • Юникод постулирует чёткое разграничение между символами, их представлением в компьютере и их отображением на устройстве вывода.
  • Юникод-символы не всегда соответствуют символу в традиционно-наивном понимании, например букве, цифре, пунктуационному знаку или иероглифу.
  • Кодовое пространство Юникода состоит из 1 114 112 кодовых позиций в диапазоне от 0 до 10FFFF.
  • Базовая многоязыковая плоскость включает в себя юникод-символы от U+0000 до U+FFFF, которые кодируются в UTF-16 двумя байтами.
  • Любая юникод-кодировка позволяет закодировать всё пространство кодовых позиций Юникода и конвертация между различными такими кодировками осуществляется без потерь информации.
  • Однобайтные кодировки позволяют закодировать лишь небольшую часть юникод-спектра, но могут оказаться полезными при работе с большим объёмом моноязыковой информации.
  • Кодировки UTF-8 и UTF-16 обладают переменной длиной кода. В UTF-8 каждый юникод-символ может быть закодирован одним, двумя, тремя или четырьмя байтами. В UTF-16 — двумя или четырьмя байтами.
  • Внутренний формат хранения текстовой информации в рамках отдельного приложения может быть произвольным при условии корректной работы со всем пространством кодовых позиций Юникода и отсутствии потерь при трансграничной передаче данных.

Краткое замечание про кодирование

С термином кодирование может произойти некоторая путаница. В рамках Юникода кодирование происходит дважды. Первый раз кодируется набор символов Юникода (character set), в том смысле, что каждому юникод-символу ставится с соответствие кодовая позиция. В рамках этого процесса набор символов Юникода превращается в кодированный набор символов (coded character set). Второй раз последовательность юникод-символов преобразуется в строку байтов и этот процесс также называется кодирование.

В англоязычной терминологии существуют два разных глагола to code и to encode, но даже носители языка зачастую в них путаются. К тому же термин набор символов (character set или charset) используется в качестве синонима к термину кодированный набор символов (coded character set).

Всё это мы говорим к тому, что имеет смысл обращать внимание на контекст и различать ситуации, когда речь идёт о кодовой позиции абстрактного юникод-символа и когда речь идёт о его байтовом представлении.

В заключение

В Юникоде так много различных аспектов, что осветить всё в рамках одной статьи невозможно. Да и ненужно. Приведённой выше информации вполне достаточно, чтобы не путаться в основных принципах и работать с текстом в большинстве повседневных задач (читай: не выходя за рамки BMP). В следующих статьях мы расскажем о нормализации, дадим более полный исторический обзор развития кодировок, побеседуем о проблемах русскоязычной юникод-терминологии, а также сделаем материал о практических аспектах использования UTF-8 и UTF-16.

Разберемся раз и навсегда: AJAX, «кириллические символы», кодировки, prototype.js, jQuery, JsHttpRequest

Как же посылать и принимать AJAX запросы в нужной нам кодировке, нужно ли использовать однобайтовые кодировки или не обойтись без UTF-8. На все эти вопросы раз и навсегда ответит эта статья.

Кстати, перепечатка с моего.

И ещё, классов-то, конечно, в JavaScript нет, но для удобства будем пользоваться такой терминологией.

В документации на XMLHttpRequest сказано, что браузер должен поддерживать следующие типы
HTTP-запросов: GET, POST, HEAD, PUT, DELETE, OPTIONS.

На сегодняшний день джаваскриптом через объект класса XMLHttpRequest можно отправить
только запросы типа GET и POST.

Итак, рассмотрим 2 этих запроса:

1. Запрос типа GET:

Вся информация скрипту на сервере может передаваться только через URL и через заголовки.

GET moy-rebenok/ajax.php?f=324
Host: moy-rebenok
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.8.1.11) Gecko/20071127
Firefox/2.0.0.11
Accept:
text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/pn
g,*/*;q=0.5
Accept-Language: ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: moy-rebenok/ajax.html

На сервере, в ajax.php можно будет использовать конструкцию
$_GET[‘f’], чтобы получить значение переменной f.

Почему встает проблема с русскими буквами? Потому что, как вы знаете, русские буквы в URL использовать нельзя, их необходимо как-то передать при помощи доступных латинских букв, цифр и знаков, допустимых в URL после знака ‘?’.

Люди договорились, что будут делать это при помощи escape-последовательностей.

escape последовательность слова «привет» в кодировке windows-1251:
%EF%F0%E8%E2%E5%F2

escape последовательность слова «привет» в кодировке UTF-8:
%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82

escape последовательность слова «привет» в кодировке KOI8-R:
%CE%CF%D5%C1%C5%D0

(Знак ‘%’, потом код символа).

Таким образом передать русские буквы можно, например, так:

Никто вас в этом не ограничивает.

Кстати, для GET запроса не нужно указывать заголовок Content-Type.
Т.к. никакого контента нет. Есть только запрос по определенному адресу.
Все переменные на сервер передаются через URL.

Как же смастерить необходимую escape последовательность в нужной кодировке?

Мастерить можно хоть руками, хоть как, но естественно в JavaScript.
Опять же, никто вас не ограничивает.

Но для удобства обычно используют одну из 3 функций, которые уже определены в JavaScript:

а) escape()
б) encodeURI()
в) encodeURIComponent()

Латинские буквы, цифры, символы @*/+. оставляет как есть, всё остальное кодирует так:
%xx, либо так: %uxxxx.
Причем, xxxx во втором случае, — это код символа не в UTF-8, а в Unicode

Использовать эту функцию не надо, т.к. результат выполнения зависит от браузера, функция не является стандартизированной W3C, возникла в лихие 90-е.

К тому же, как-то нормально (по крайней мере, быстро) обработать строку в таком винигретчатом формате на сервере сложно.

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

Латинские буквы, цифры, символы !@#$&*()=:/;?+’. оставляет как есть, всё остальное
кодирует
escape-последовательностями в кодировке UTF-8.

Латинские буквы, цифры, символы !*()’. оставляет как есть, всё остальное кодирует
escape-последовательностями в кодировке UTF-8.
Одобрено W3C.

Используется jQuery, prototype.js при запросе методом GET.

Возможно вы слышали от кого-то: «XMLHttpRequest работает только с UTF-8».
Теперь знаете, что это не совсем правда.

Когда используется GET-запрос, то кодировка переданных данных вообще нигде не прописывается(!).
Ещё раз повторю, ‘Content-type’, в котором мы можем указать charset
не используется в GET запросах.

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

Именно поэтому в jQuery даже нельзя никак указать charset при отправке запроса.
Именно поэтому в Prototype.js, даже когда указываешь encoding=’windows-1251′, и используешь GET запрос, то передается всё равно UTF-8.

Просто потому что в кодах этих библиотек используется функция encodeURIComponent().

Что ж. В этом нет совершенно ничего плохого. Всё, что надо сделать, чтобы теперь работать
в PHP в
нормальной кодировке использовать iconv:

$f = iconv(‘UTF-8’, ‘windows-1251’, $_GET[‘f’]);

Кстати, мы можем это сделать именно потому, что $_GET работает так, что он понимает
escape-последовательности. Спасибо создателям PHP.

Т.е. когда приходит GET запрос PHP смотрит на URL, создает для нас массив $_GET, а мы
уже с ним
что хотим, то и делаем. Но это вроде понятно должно быть.

2) POST-запросы.

Здесь уже всё интереснее.

Вот приходит это запрос на сервер. Обработчик PHP смотрит на Content-type, и в зависимости от него заполняет массив $_POST и/или переменную $HTTP_RAW_POST_DATA.

$_POST он заполняет в том случае, когда в Content-type указано multipart/form-data или
x-www-form-urlencoded.

Что-же это за Content-type такой?
А контент-тайп это очень удобный. Он позволяет передать php скрипту несколько переменных.

Что по сути такое POST запрос?
Это заголовки, а за ними контент. Контент вообще произвольный. Т.е. просто байты, байты, байты.

Но ведь из JavaScript обычно требуется передать не просто байты, байты, байты, а несколько пар ключ=значение, ключ=значение,…
Как в GET запросе.

Вот люди и договорились о таком удобном типе, как x-www-form-urlencoded
Для того, чтобы передать f=123 и gt=null необходимо передать контент:

Знакомо неправда ли? Конечно знакомо, и тип не зря называется x-www-form-urlencoded.
Всё то же самое, что и при GET запросе.

И как же формируется контент в библиотеках jQuery и prototype.js?

Верно, при помощи всё той же функции encodeURIComponent(), а значит и escape-последовательности будут в кодировке UTF-8. (Независимо от того, что в prototype.js вы установите encoding).

Всё. Осталась ещё одна возможность. Ведь можно передавать не x-www-form-urlencoded (т.е. не параметры), а обычный текстовый или бинарный контент, который потом можно будет прочитать через $HTTP_RAW_POST_DATA.

Для этого устанавливаем Content-type text/xml или application/octet-stream, там же устанавливаем charset=«windows-1251».

Засовываем в функцию send() строку нужной кодировки. (Prototype.js оборачивает этот вызов конструкцией new Ajax.Request(. )).

И что потом… А он (объект класса XMLHttpRequest) переводит эту строку в UTF-8, в какой бы кодировке она не была. Так написано в документации W3C. И он реально это делает.

1. Напрямую через XMLHttpRequest можно передавать только строки в кодировке UTF-8.

2. Можно передавать строки как бы «в любых других кодировках», если нелатинские символы
при этом за-escape-ены.

3. В JavaScript существует 3 функции, которые escape-ят нелатинские символы:
escape(), encodeURI() и encodeURIComponent().

Первая переводит в кривой Unicode. Вторые две в UTF-8.

Можно написать свои функции, которые будут генерировать escape-последовательности любой кодировки. Можно, но не нужно. Т.к. наоборот надо радоваться, что есть такие вот функции, которые переводят текст любой кодировки в UTF-8. Это черезвычайно прекрасный факт. Схема при которой все xhtml страницы работают на windows-1251, ajax с сервера клиенту кидает windows-1251, а ajax с клиента серверу кидает UTF-8 абсолютна приемлема и используется на большинстве ресурсов.

Просто не надо забывать использовать iconv как было описано ниже. А для того, чтобы сервер отдавал яваскрипту JSON (или что там у вас) в правильной кодировке (т.е. в такой же кодировке, в которой отдаются все xhtml страницы) просто в начале вашего ajax.php пропишите заголовок:

header(‘Content-type: text/html; charset=windows-1251’);

На последок немного субъективного мнения:

Используйте jQuery, любите людей, дарите подарки.

Шпаргалка по кодировке символов для JavaScript разработчиков

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

Как JavaScript разработчик, вы постоянно работаете с текстовыми данными. Однако компьютеры по своей природе не понимают текст так же как люди. Вместо этого текстовые данные должны быть преобразованы в двоичные, которые компьютеры могут понимать и обрабатывать. Кодирование символов — процесс преобразования символов в двоичные данные, позволяющий компьютерам работать с текстом. Кодирование символов важно потому, что оно позволяет хранить и передавать текст в электронном виде. Оно используется в самых разных приложениях — от веб-разработки до хранения и передачи данных. Для того чтобы различные компьютеры и устройства могли работать вместе, они должны использовать одну и ту же схему кодирования. В контексте JavaScript разработки кодирование символов особенно важно, поскольку JavaScript часто используется для манипулирования и отображения текстовых данных в веб-приложениях.

В этой статье мы рассмотрим кодировку символов в JavaScript, включая как Node.js, так и браузерную часть. Мы начнём с введения в кодировку символов и стандарта кодировки символов Unicode, который стал стандартом де-факто для кодирования текстовых данных в современных компьютерах. Далее мы подробно рассмотрим кодировку ASCII, стандарт кодирования, предшествовавший Unicode, и то, как Unicode в значительной степени заменил его. Затем мы рассмотрим кодировку UTF-8, которая является наиболее распространённым стандартом кодирования символов в JavaScript и других современных языках программирования. К концу статьи вы будете иметь полное представление о кодировке символов в JavaScript и вооружитесь знаниями, необходимыми для эффективной работы с текстовыми данными в ваших JavaScript-приложениях. Давайте начнём!

Понимание Unicode

Unicode/Юникод — стандарт кодирования символов, разработанный для решения проблемы, связанной с тем, что предыдущие схемы кодирования не могли представить все символы во всех языках. Он обеспечивает уникальное число, называемое кодовой точкой, для каждого символа, знака и эмодзи в каждом языке. Unicode стал фактическим стандартом для кодирования символов в современных компьютерах и в значительной степени заменил более старые стандарты кодирования, такие как ASCII. Для работы с Unicode в JavaScript важно понимать его структуру и отличия от других стандартов кодирования.

Кодировка символов ASCII

Американский стандартный код для обмена информацией ( ASCII ) был первым широко распространённым стандартом кодирования символов. Он был разработан в 1960-х годах и основывался на английском алфавите и некоторых распространённых знаках препинания. Однако ASCII имел ряд недостатков, в том числе неспособность представлять символы других языков и ограниченный набор символов. В результате он был в значительной степени заменён на Unicode. Хотя в процессе разработки JavaScript вы, возможно, не часто сталкиваетесь с ASCII, важно знать его историю и ограничения, чтобы лучше понимать, как развивалась кодировка символов с течением времени.

Кодировка символов UTF-8

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

Кодировка символов UTF-16

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

Следует отметить, что в браузере кодирование символов обычно задаётся с помощью метатега HTML. Метатег включается в раздел head каждой HTML-страницы для обеспечения корректного отображения символов, отличных от ASCII.

DOCTYPE html> 
html>
head>
meta charset="utf-8">
title>My Web Pagetitle>
head>
body>
p>こんにちはp>
body>
html>

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

const string = 'こんにちは、世界!'; // Строка на японском языке 
const encodedString = encodeURIComponent(string);
console.log(encodedString);

В этом примере мы начинаем с создания строковой переменной string , которая содержит японское приветствие こんにちは、世界! , что в переводе на русский означает Здравствуй, мир! . Затем с помощью функции encodeURIComponent() кодируем переменную string как URI-компонент с использованием кодировки UTF-8. Эта функция кодирует каждый символ в строке как последовательность от одного до четырёх байт в зависимости от его кодовой точки Unicode. Наконец, выводим переменную encodedString в консоль с помощью функции console.log() , которая выводит на консоль URI-кодированную версию исходной строки:

%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF%E3%80%81%E4%B8%96%E7%95%8C%EF%BC%81

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

Для декодирования строки, закодированной в URI, в JavaScript можно использовать функцию decodeURIComponent() . Она преобразует закодированные с помощью процентов байты обратно в исходные символы.

const decodedString = decodeURIComponent(encodedString); 
console.log(decodedString);

В приведённом выше коде переменная encodedString содержит URI-кодированную версию японской строки приветствия. С помощью функции decodeURIComponent() мы декодируем байты с процентным кодированием из encodedString обратно в исходные символы с использованием кодировки UTF-8. Эта функция обращает процесс кодирования URI в обратную сторону и возвращает декодированную строку. Наконец, выводим переменную decodedString в консоль с помощью функции console.log() , которая выводит исходную строку японского приветствия:

こんにちは、世界!

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

Ajax-запросы

Также можно отправлять и получать данные с помощью AJAX. В этом случае необходимо указать кодировку символов, используемую сервером. Это можно сделать с помощью параметра charset заголовка Content-Type :

const xhr = new XMLHttpRequest(); 
xhr.open('GET', '/data', true);
xhr.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
xhr.onload = () =>
const data = JSON.parse(xhr.responseText);
console.log(data);
>;
xhr.send();

Формы браузера и файлы cookie

Работа с файлами cookie и данными форм в браузере также требует обработки кодировки символов. Cookie следует кодировать с помощью URL-кодирования, для обеспечения корректной передачи и декодирования сервером. Данные формы могут быть отправлены с помощью методов GET или POST , а кодировка символов должна быть указана с помощью атрибута enctype формы:

script> 
document.cookie = `name=$encodeURIComponent('John Doe')>; expires=$expires>; path=/`; script>

form action="/submit" method="post" enctype="application/x-www-form-urlencoded">
input type="text" name="name" value="John Doe">
button type="submit">Submitbutton>
form>

Кодировка символов в Node.js

Node.js — популярная среда выполнения JavaScript, позволяющая создавать серверные приложения на JavaScript. При работе с текстовыми данными в Node.js важно понимать, как работает кодировка символов. В Node.js текстовые данные обычно представляются в виде строк. Однако в JavaScript строки кодируются с использованием стандарта UTF-16, что может вызвать проблемы при работе с другими кодировками. При чтении и записи текстовых файлов в Node.js можно указать тип кодировки символов с помощью модуля fs . Например, для чтения файла в кодировке UTF-8 можно использовать следующий код:

const fs = require('fs'); 

fs.readFile('myfile.txt', 'utf-8', (err, data) =>
if (err) throw err;
console.log(data);
>);

В приведённом выше коде мы используем встроенный в Node.js модуль fs , что означает file system , для чтения содержимого файла myfile.txt . Начнём с импорта модуля fs с помощью функции require и присвоения его переменной fs . Затем используем метод readFile , предоставляемый модулем fs , для чтения содержимого файла myfile.txt . Этот метод принимает три аргумента: путь к файлу для чтения (в данном случае myfile.txt ), кодировку для чтения файла (в данном случае utf-8 ) и функцию обратного вызова, которая будет вызвана, когда файл будет прочитан.

Функция обратного вызова принимает два параметра: параметр err , содержащий объект ошибки, если при чтении файла произошла ошибка, и параметр data , который содержит содержимое файла в виде строки, если файл был успешно прочитан. В функции обратного вызова сначала проверяем, не произошла ли ошибка при чтении файла, проверяя, является ли параметр err равным null . Если ошибка произошла, то выбрасываем исключение, объект error , в противном случае выводим содержимое файла на консоль с помощью функции console.log .

При работе с HTTP-запросами и ответами в Node.js можно также указать тип кодировки символов с помощью заголовка Content-Type . Например, чтобы отправить ответ в формате UTF-8, можно использовать следующий код:

const http = require('http'); 

http.createServer((req, res) =>
res.writeHead(200, 'Content-Type': 'text/html; charset=utf-8' >);
res.write('Hello, 世界!');
res.end();
>).listen(3000);

Приведённый выше код отправляет ответ: «Hello». Обратите внимание, что в заголовке Content-Type мы указываем кодировку ответа.

При работе с базами данных в Node.js также может потребоваться указать кодировку символов для подключения. Например, при использовании модуля mysql можно указать кодировку символов в параметрах соединения:

const mysql = require('mysql'); 

const connection = mysql.createConnection(
host: 'localhost',
user: 'me',
password: 'secret',
database: 'mydb',
charset: 'utf8mb4' //здесь мы указываем кодировку символов для соединения с mysql
>);

connection.connect();

Наконец, важно знать об ошибках кодировки символов в Node.js. При работе с текстовыми данными возможно появление ошибок, связанных с неправильной кодировкой или неверным формированием данных. Для работы с такими ошибками можно использовать модуль iconv-lite , предоставляющий утилиты для кодирования и декодирования текстовых данных:

const iconv = require('iconv-lite'); 
const buf = iconv.encode('Hello, world!', 'utf-8');
const str = iconv.decode(buf, 'utf-8');
console.log(str); // Результат: Hello, world!

В приведённом выше коде мы используем библиотеку iconv-lite для кодирования и декодирования строки с использованием кодировки utf-8 . Сначала мы импортируем библиотеку iconv-lite с помощью функции require и присваиваем её переменной iconv . Далее создаём новый объект Buffer под названием buf , кодируя строку Hello, world! в кодировке utf-8 с помощью функции iconv.encode . Затем создаём новую строку str , декодируя буфер buf в кодировке utf-8 с помощью функции iconv.decode . Наконец, выводим строку str в консоль с помощью функции console.log .

Лучшие практики кодировки символов

При работе с кодировкой символов в JavaScript следует помнить о нескольких рекомендациях/лучших практиках:

  • Используйте кодировку UTF-8 для всех текстовых данных. Это наиболее распространённый и совместимый стандарт кодирования, обеспечивающий правильное чтение и отображение данных на различных платформах и языках.
  • Избегайте использования в именах переменных и функций символов, отличных от ASCII. Хотя технически это разрешено в JavaScript, это может привести к несовпадению кодировок и затруднить чтение и понимание кода.
  • При конкатенации строк всегда используйте оператор + или шаблонные литералы ( $<> ) вместо оператора += . Это позволяет предотвратить несоответствие кодировок символов и обеспечить корректную работу кода с символами, отличными от ASCII.
  • При работе с пользовательским вводом всегда проверяйте и очищайте данные для предотвращения атак на кодировку и других уязвимостей безопасности. Для этого можно использовать, например, библиотеку «OWASP ESAPI» или «DOMPurify».
  • При работе с внешними API или базами данных всегда проверяйте предполагаемую кодировку символов и при необходимости преобразуйте данные в UTF-8. Это позволит избежать несоответствия кодировок символов и обеспечить правильное хранение и извлечение данных.

Отладка проблем кодировки символов

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

Одним из подходов к отладке проблем с кодировкой символов является использование такого инструмента, как UTF-8 Validator, позволяющего выявить типичные проблемы с кодировкой символов. Кроме того, можно использовать операторы console.log для вывода кодированных и декодированных данных, а затем сравнить их, для выявления различий. Ещё одним полезным приёмом является использование такого инструмента, как библиотека iconv , позволяющая преобразовывать данные между различными кодировками. Это может быть особенно полезно при работе с данными из внешних источников, которые могут использовать кодировку символов, отличную от кодировки символов вашего приложения.

Заключение и дополнительные материалы

В этой статье мы рассмотрели основы кодирования символов в JavaScript, включая различные стандарты кодирования, принцип их действия и работу с ними в Node.js и веб-браузерах. Также рассмотрели несколько рекомендаций/лучших практик работы с кодировкой символов в JavaScript и дали советы и методы отладки проблем с кодировкой символов. Если вы хотите узнать больше о кодировке символов в JavaScript, есть несколько ресурсов. На сайте Консорциума Unicode представлена подробная информация о стандарте Unicode, а на сайте Mozilla Developer Network имеется обширная документация по кодировке символов в JavaScript. Кроме того, существует несколько книг по JavaScript, в которых подробно рассматривается эта тема, например, JavaScript: The Definitive Guide David Flanagan и Eloquent JavaScript Marijn Haverbeke.

Методы и свойства строк

По аналогии с числами у строк тоже есть свой набор методов, упрощающих работу с ними. И, как вы уже догадались, на этом занятии мы будем рассматривать методы работы со строками. Но в начале отметим, что все строки в JavaScript представлены в кодировке UTF-16, даже если в браузере HTML-документ имеет другую кодировку.

Итак, поехали. Мы с вами уже говорили о разнообразии кавычек для представления строковых литералов, например:

let str1 = "Hello World!"; let str2 = 'Hello World!'; let str3 = `Hello World!`;

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

let name = "Java", age = 18; let str = `Меня зовут ${name}, мне ${age} лет`; console.log( str );
  • \n – спецсимвол перевода на новую строку;
  • \r – возврат каретки (используется в ОС Windows совместно с символом \n);
  • \t – спецсимвол табуляции;
  • \uXXXX – символ в кодировке UTF-16;
  • \» и \’ – символы кавычек (внутри строки);
  • \\ — символ обратного слеша.
let str = 'Hello!\nI\'m Javascript.\nВот мой символ \t табуляции, обратный слеш \\ и символ \u00A9 копирайта'; console.log( str );

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

length

Свойство length содержит длину строки, например:

let str = 'Hello!\nI\'m Javascript.'; console.log( str.length );

Увидим в консоле длину строки str. Обратите внимание, что length – это свойство, а не метод, поэтому вот такое обращение str.length() работать не будет.

[] и charAt

Из любой строки можно взять отдельный символ. Это делается с помощью оператора [], в которых указывается индекс нужного символа. Например:

let str = 'Hello!\nI\'m Javascript.'; let ch1 = str[0]; let ch2 = str[7]; console.log(ch1, ch2); console.log(typeof ch1);

Обратите внимание, что так как в JavaScript нет символьного типа, то возвращаемое значение – это строка, состоящая из одного символа. Ту же самую операцию выделения символа можно выполнить и с помощью метода charAt(pos), но он менее удобен и существует, скорее по историческим причинам для совместимости со старыми скриптами:

let ch1 = str.charAt(0); let ch2 = str.charAt(7);

Интересной особенностью JavaScript является возможность перебрать строку посимвольно с помощью цикла for of, используемого для массивов:

for(let ch of "Hello") console.log(ch);

Обратите внимание, что строки в JavaScript изменять нельзя. Например, нельзя выполнить такую операцию:

str[0] = "h";

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

toLowerCase() и toUpperCase()

Данные методы возвращают строку в нижнем и верхнем регистрах. Например:

let str = 'Hello!'; let low = str.toLowerCase(); let hi = "string".toUpperCase(); console.log(low, hi);

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

infexOf и lastIndexOf

Данный метод выполняет поиск подстроки substr, начиная с позиции pos: str.indexOf(substr[, pos]) возвращает позицию, на которой располагается совпадение, либо -1 если совпадений не найдено.

let str = '; let indx1 = str.indexOf("clock"); // 13 let indx2 = str.indexOf("span", 2); // 27 let indx3 = str.indexOf("div"); // -1 console.log(indx1, indx2, indx3);

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

let indx = -1; while(true) { indx = str.indexOf("span", indx+1); if(indx == -1) break; console.log(indx); }

Другой похожий метод str.lastIndexOf(substr, position) ищет подстроку с конца строки к началу. Он используется тогда, когда нужно получить самое последнее вхождение:

let indx = str.lastIndexOf("span"); console.log(indx);

includes, startsWith, endsWith

Следующие три метода позволяют проверять: есть ли в строке искомая подстрока. Первый метод имеет ожидаемый синтаксис: str.includes(substr[, pos]) он возвращает true, если подстрока substr была найдена в строке str и false в противном случае. Второй необязательный параметр pos указывает начальный индекс для поиска. Вот примеры использования данного метода:

let str = '; console.log( str.includes("span") ); console.log( str.includes("") ); console.log( str.includes("clock", 20) );

Следующие два метода startsWith и endsWith проверяют, соответственно, начинается ли и заканчивается ли строка определённой строкой:

console.log( str.startsWith("span") ); //false console.log( str.startsWith(") ); //true console.log( str.endsWith("span>") ); //true

slice

Есть три основных метода для выделения подстрок из строки – это substring, substr и slice. Метод slice имеет следующий синтаксис: str.slice(start [, end]) и возвращает часть строки от start до end (не включая его). Например:

console.log( str.slice(0, 5) ); // console.log( str.slice(6, 11) ); //class console.log( str.slice(12) ); //"clock". console.log( str.slice(-7, -1) ); //

Следующий метод str.substring(start [, end]) работает практически также как и slice, но здесь аргумент start может быть больше, чем end, например:

console.log( str.substring(6, 11) ); //class console.log( str.substring(11, 6) ); //class

Но отрицательные значения записывать нельзя, они будут трактоваться как 0. Последний метод str.substr(start [, length]) Возвращает часть строки, начиная с индекса start и длиной в length символов. В противоположность предыдущим методам, здесь указывается длина вместо конечной позиции:

console.log( str.substr(6, 13) ); //class = "clock" console.log( str.substr(12) ); //"clock">12:34

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

Нюансы сравнения строк

Если мы проверяем строки на равенство, то никаких особых проблем в JavaScript это не вызывает, например:

if("abc" == "abc") console.log( "строки равны" ); if("abc" != "ABC") console.log( "строки не равны" );

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

console.log( "z" > "Za" ); //true

2. Если код текущего символа одной строки меньше кода текущего символа другой строки, то первая строка меньше второй:

console.log( "B"  "a" ); //true

3. При равенстве символов больше та строка, которая содержит больше символов:

console.log( "abc"  "abcd" ); //true

4. В остальных случаях строки равны:

console.log( "abc" == "abc" ); //true

Но в этом алгоритме есть один нюанс. Например, вот такое сравнение:

console.log( "Америка" > "Japan" ); //true

Дает значение true, так как русская буква A имеет больший код, чем латинская буква J. В этом легко убедиться, воспользовавшись методом

str.codePointAt(pos)

который возвращает код символа, стоящего в позиции pos:

console.log( "А".codePointAt(0), "J".codePointAt(0) );

Сморите, у буквы А код равен 1040, а у буквы J – 74. Напомню, что строки в JavaScript хранятся в кодировке UTF-16. К чему может привести такой результат сравнения? Например, при сортировке мы получим на первом месте страну «Japan», а потом «Америка». Возможно, это не то, что нам бы хотелось? И здесь на помощь приходит специальный метод для корректного сравнения таких строк:

str.localeCompare(compareStr)

он возвращает отрицательное число, если str < compareStr, положительное при str >compareStr и 0 если строки равны. Например:

console.log( "Америка".localeCompare("Japan") ); // -1

возвращает -1 как и должно быть с учетом языкового сравнения. У этого метода есть два дополнительных аргумента, которые указаны в документации JavaScript. Первый позволяет указать язык (по умолчанию берётся из окружения) — от него зависит порядок букв. Второй позволяет определять дополнительные правила, например, чувствительность к регистру.

Некоторые другие методы

У строковых переменных есть еще пара полезных и часто используемых методов, это: str.trim() убирает пробелы в начале и конце строки:

let str = " string "; console.log( str.trim() );

И str.repeat(n) для повторения строки n раз:

let str = "Abc"; console.log( str.repeat(5) );

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

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

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