коды английских букв java
Java: Кодировка
Обычные, привычные нам числа в десятичной системе счисления, закодированы с помощью двоичных чисел:
Но как быть с текстом? Компьютер на самом деле не знает ничего о буквах, знаках пунктуации и прочих текстовых символах. Все эти символы так же закодированы числами.
Можно взять английский алфавит и дать каждой букве число, начиная с единицы по порядку:
Далее можно научить компьютер понимать эту таблицу и переводить текст в числа и наоборот — числа в текст:
В этом заключается суть кодировок.
Кроме букв алфавита, в таблицы кодировок входят знаки препинания и другие полезные символы. Вы наверняка сталкивались с кодировками, например, ASCII или UTF-8.
С распространением компьютеров, разным странам понадобились свои, более широкие таблицы. В том числе для кириллических букв, восточных иероглифов, арабской вязи, дополнительных математических и типографских символов, а впоследствии даже для эмодзи-смайликов.
Сегодня в большинстве случаев используется один из вариантов юникода, включающий в себя знаки почти всех письменных языков мира.
Задание
В Java можно «запросить» и вывести на экран любой символ из кодировки ASCII. Например:
Найдите в интернете таблицу кодов ASCII. Можно использовать запросы типа «ascii codes table» или «коды ascii». Обычно в таких таблицах коды указаны сразу в нескольких системах счисления: десятичной, двоичной, восьмеричной и шестнадцатеричной. Нас интересует десятичный код (dec или decimal).
Используя пример выше и найденную таблицу, выведите на экран
Алфавит языка программирования Java и Unicode
Мне кажется, что этот вопрос скорее из плоскости философии, но всё равно было бы неплохо разобраться. Итак, начну немного издалека, чтобы была понятна суть. Абсолютно любой язык программирования, в том числе и упомянутый в заголовке Java, является формой формального языка, который предназначен для записи компьютерных программ. Если говорить грубо, то формальный язык представляет собой математическую модель реального языка и содержит набор правил, которые позволяют определить язык, в том числе множество грамматик (в иерархии Хомского выделено 4 типа формальных грамматик), предназначенных для этих целей. Разумеется, что каждый язык имеет свой алфавит, так что формальные языки не выпадают из этого правила и также подразумевают наличие некоторого множества атомарных символов, которые позволят выстраивать слова на этом языке и выступать в качестве основы терминальных символов. Запомним этот момент, так как он нам ещё пригодится.
Как только мы начинаем знакомиться с JLS, то уже во второй главе узнаём о том, что Java относится к формальным языкам с контекстно-свободной грамматикой, что и не сильно удивляет, так как подавляющее большинство языков программирования описывается именно при помощи этого типа формальной грамматики. Какую основную мысль мы можем выделить из этого для себя? Если мы имеем дело с КС-грамматикой, то любую лексическую и синтаксическую структуру, мы будем описывать при помощи продукций, в левой части которых всегда будет находиться исключительно один нетерминальный символ (отсутствует окружающий его контекст), который может быть выражен через некоторое сочетание, как терминальных, так и нетерминальных символов, начиная с целевого символа.
Так какую же величину имеет наш алфавит? 1,114,112 символов (если охватывать всё кодовое пространство Unicode)? 1,112,064, если вычесть из этого количества суррогаты? Или и вовсе 143,924 символа, если брать в расчёт исключительно символы, которые имеют графическое представление, а также форматирующие и управляющие. И то, это будет справедливо для Unicode 13.0. А если учитывать тот факт, что класс java.lang.Character в Java SE 14 поддерживает лишь версию Unicode 12.1, то правильно ли говорить, что в нашем распоряжении ещё меньше символов, а точнее 137,994 (именно столько символов однозначно определено в этой версии Unicode)? Этот вопрос всегда волновал, так как неопределённость мне не очень нравится и всегда хочется дать чёткий ответ на поставленный вопрос. Лично я считаю, что мы можем использовать абсолютно все кодовые позиции так, как посчитаем нужным (такая возможность есть, но так лучше не делать). Именно поэтому, если мы больше говорим о теории, то мне кажется, что мы упираемся лишь в ограничения самого стандарта и кодировки UTF-16. Согласны со мной? Или я всё-таки не совсем верно рассуждаю и 1,114,112 символов далеки от истины? Интересно Ваше мнение по данному вопросу! 🙂
2 ответа 2
Неверно вы понимаете, согласно спецификации Java идентификаторы могут быть любыми Unicode символами (ну кроме специально зарезервированных)
В принципе, можно написать примерно так:
Правда в компиляторе надо указать кодировку, типа:
Update
Если честно, мне как все равно, сколько символов составляет алфавит Java: 125 тыс или 129 тыс. или там миллион. Это же типичная проблема точного определения, как только вы озаботитесь точным определением, границы сразу же начинают размываться.
То же самое и здесь. Уверен, что 99.9% Java программистов успешно живут используя для Java строго только латинский алфавит из набора alpha-numeric и абсолютно счастливы.
Понимаю, что есть Хомский, есть философия и т.д., но я бы таки призвал слишком далеко не улетать и остаться в рамках latin alphanumeric
Распишу и своё собственное видение по данному вопросу. Немного покумекал и судя по всему добрался до истины.
Итак, чтобы полностью вникнуть в этот вопрос, необходимо ещё раз обратиться к теории. Если рассматривать язык программирования Java с точки зрения лексической структуры, то мы можем выделить 5 основных видов токенов, которые являются входными элементами. Именно из последовательности этих входных элементов и состоит вся наша программа, а сами элементы представляют собой основу лексической грамматики языка и в то же время выступают в роли терминальных символов синтаксической грамматики. Всю информацию можно найти в соответствующем разделе Java Language Specification, а я лишь приведу небольшую сноску.
The input characters and line terminators that result from escape processing (§3.3) and then input line recognition (§3.4) are reduced to a sequence of input elements.
Таким образом, мы можем видеть, что сколь-нибудь значащими входными элементами являются лишь токены, так как пробельные символы (в том числе и разделители строк, которые с точки зрения спецификации также относятся к пробельным символам) и комментарии полностью игнорируются компилятором в процессе генерации байт-кода. Давайте взглянем на ещё одну базовую продукцию и немного ознакомимся с алгоритмом трансляции символов во входном потоке.
A compiler for the Java programming language («Java compiler») first recognizes Unicode escapes in its input, translating the ASCII characters \u followed by four hexadecimal digits to the UTF-16 code unit (§3.1) for the indicated hexadecimal value, and passing all other characters unchanged. Representing supplementary characters requires two consecutive Unicode escapes. This translation step results in a sequence of Unicode input characters.
Здесь необходимо обратить внимание на то, что наши Unicode-символы, которые и являются основной составляющей входных элементов, формирующихся после отбрасывания пробельных символов и комментариев, также проходят через процесс трансляции, если они были заданы при помощи управляющих последовательностей Unicode. Но здесь всё достаточно просто и очевидно, поэтому остановимся на необработанных входных символах, которые и интересуют нас больше всего. Именно из этой продукции становится понятно, что мы можем использовать в качестве этих символов «любые Unicode-символы».
Но вернёмся к нашим токенам, чтобы понять, какое количество Unicode-символов мы могли бы использовать для каждого отдельного вида наших терминальных символов синтаксической грамматики. Мы могли бы написать нашу программу при помощи одних лишь управляющих последовательностей Unicode, что могло бы сократить набор используемых символов до такой степени, что он был бы меньше, чем тот набор, который предлагает нам ASCII. Но, как не крути, все управляющие последовательности всё равно проходят через процесс трансляции и мы получаем на выходе конкретный Unicode-символ (может быть в пределах ASCII, так и далеко за его границами). Если говорить о ключевых словах, операторах и разделителях, то перечень этих токенов жёстко установлен спецификацией и мы никоим образом не можем его расширять. Соответственно, мы уже можем прикинуть, сколько уникальных символов нам могло бы понадобиться для записи каждого из них. Пока ничего сложного нет, так как мы не вышли даже за пределы ASCII. Что касается идентификаторов, то тут всё зависит от версии платформы Java SE, которую мы используем. Количество уникальных Unicode-символов, которые мы можем использовать при составлении наших идентификаторов постоянно растёт и составляет 129,123 символа для Java SE 14 (поддерживаются символы описанные в стандарте Unicode 12.1), если символ просто является частью идентификатора, но не начинается с него. И 125,951 символ, которым мы можем начинать наши идентификаторы. Проверить всё это можно при помощи соответствующих методов класса java.lang.Character. Очевидным является тот факт, что мы не можем использовать в качестве идентификаторов ключевые слова (в том числе ограниченные ключевые слова в процессе описания модуля), разделители и операторы, а также литералы логического типа и null.
Руководство по кодировке символов
Изучите кодировку символов в Java и узнайте о распространенных подводных камнях.
1. Обзор
В этом уроке мы обсудим основы кодирования символов и то, как мы справляемся с этим в Java.
2. Важность кодирования символов
Нам часто приходится иметь дело с текстами, принадлежащими к нескольким языкам с различными письменными знаками, такими как латинский или арабский. Каждый символ в каждом языке должен быть каким-то образом сопоставлен с набором единиц и нулей. Действительно, удивительно, что компьютеры могут правильно обрабатывать все наши языки.
Чтобы сделать это правильно, нам нужно подумать о кодировке символов. Невыполнение этого требования часто может привести к потере данных и даже уязвимостям безопасности.
Чтобы лучше понять это, давайте определим метод декодирования текста на Java:
Обратите внимание, что вводимый здесь текст использует кодировку платформы по умолчанию.
Ну, не совсем то, что мы ожидали.
Что могло пойти не так? Мы постараемся понять и исправить это в оставшейся части этого урока.
3. Основы
3.1. Кодирование
Например, первая буква в нашем сообщении, “T”, в US-ASCII кодирует в “01010100”.
3.2. Кодировки
3.3. Кодовый пункт
Кодовая точка-это абстракция, которая отделяет символ от его фактической кодировки. A кодовая точка – это целочисленная ссылка на определенный символ.
Мы можем представить само целое число в простых десятичных или альтернативных основаниях, таких как шестнадцатеричное или восьмеричное. Мы используем альтернативные базы для удобства ссылки на большие числа.
Например, первая буква в нашем сообщении, T, в Юникоде имеет кодовую точку “U+0054” (или 84 в десятичной системе счисления).
4. Понимание Схем Кодирования
Кодировка символов может принимать различные формы в зависимости от количества символов, которые она кодирует.
Количество закодированных символов имеет прямое отношение к длине каждого представления, которое обычно измеряется как количество байтов. Наличие большего количества символов для кодирования по существу означает необходимость более длинных двоичных представлений.
Давайте рассмотрим некоторые из популярных схем кодирования на практике сегодня.
4.1. Однобайтовое кодирование
Одна из самых ранних схем кодирования, называемая ASCII (Американский стандартный код для обмена информацией), использует однобайтовую схему кодирования. По сути, это означает, что каждый символ в ASCII представлен семибитными двоичными числами. Это все еще оставляет один бит свободным в каждом байте!
Ascii 128-символьный набор охватывает английские алфавиты в нижнем и верхнем регистрах, цифры и некоторые специальные и контрольные символы.
Давайте определим простой метод в Java для отображения двоичного представления символа в определенной схеме кодирования:
Теперь символ ” T ” имеет кодовую точку 84 в US-ASCII (ASCII в Java называется US-ASCII).
И если мы используем наш метод утилиты, мы можем увидеть его двоичное представление:
Это, как мы и ожидали, семиразрядное двоичное представление символа “T”.
Исходный ASCII оставил самый значимый бит каждого байта неиспользованным. В то же время ASCII оставил довольно много непредставленных символов,
Исходный ASCII оставил самый значимый бит каждого байта неиспользованным. || В то же время ASCII оставил довольно много непредставленных символов,
Было предложено и принято несколько вариантов схемы кодирования ASCII.
Многие расширения ASCII имели разные уровни успеха, но, очевидно, это
4.2. Многобайтовое кодирование
Поскольку потребность в размещении все большего количества символов росла, однобайтовые схемы кодирования, такие как ASCII, не были устойчивыми.
Это привело к появлению многобайтовых схем кодирования, которые имеют гораздо большую емкость, хотя и за счет увеличения требований к пространству.
Давайте теперь вызовем метод convertToBinary с вводом как “語”, китайский символ, и кодирование как “Big5”:
Вывод выше показывает, что кодировка Big5 использует два байта для представления символа “語”.
полный список кодировок символов, наряду с их псевдонимами, ведется Международным органом по номерам.
5. Юникод
Нетрудно понять, что, хотя кодирование важно, декодирование в равной степени жизненно важно для понимания представлений. Это возможно на практике только в том случае, если широко используется согласованная или совместимая схема кодирования.
Различные схемы кодирования, разработанные изолированно и практикуемые в местных географических регионах, начали становиться сложными.
Ну, для этого должно потребоваться несколько байтов для хранения каждого символа? Честно говоря, да, но у Unicode есть гениальное решение.
Unicode как стандарт определяет кодовые точки для каждого возможного символа в мире. Кодовая точка для символа “T” в Юникоде равна 84 в десятичной системе счисления. Обычно мы называем это “U+0054” в Юникоде, который представляет собой не что иное, как U+, за которым следует шестнадцатеричное число.
Мы используем шестнадцатеричную систему в качестве основы для кодовых точек в Юникоде, поскольку существует 1 114 112 точек, что является довольно большим числом для удобной передачи в десятичном формате!
То, как эти кодовые точки кодируются в биты, зависит от конкретных схем кодирования в Юникоде. Мы рассмотрим некоторые из этих схем кодирования в подразделах ниже.
5.1. UTF-32
Вывод выше показывает использование четырех байтов для представления символа “T”, где первые три байта-это просто потраченное впустую пространство.
5.2. UTF-8
Давайте снова вызовем метод convertToBinary с вводом как “T” и кодированием как ” UTF-8″:
Вывод в точности аналогичен ASCII, использующему только один байт. На самом деле UTF-8 полностью обратно совместим с ASCII.
Давайте снова вызовем метод convertToBinary с вводом как “語” и кодированием как ” UTF-8″:
Как мы видим здесь, UTF-8 использует три байта для представления символа “語”. Это известно как кодирование переменной ширины .
UTF-8, благодаря своей экономичности пространства, является наиболее распространенной кодировкой, используемой в Интернете.
6. Поддержка кодирования в Java
Есть некоторые тонкости в том, как Java подбирает кодировку для работы. Давайте рассмотрим их более подробно.
6.1. Кодировка по умолчанию
Это зависит от локали и кодировки базовой операционной системы, на которой работает JVM. Например, в macOS кодировка по умолчанию-UTF-8.
Давайте посмотрим, как мы можем определить кодировку по умолчанию:
Если мы запустим этот фрагмент кода на компьютере с Windows, то получим результат:
Теперь “windows-1252” – это кодировка по умолчанию платформы Windows на английском языке, которая в данном случае определила кодировку по умолчанию JVM, работающей в Windows.
6.2. Кто использует Кодировку по умолчанию?
Многие API Java используют кодировку по умолчанию, определенную JVM. Чтобы назвать несколько:
Итак, это означает, что если бы мы запустили наш пример без указания кодировки:
затем он будет использовать кодировку по умолчанию для ее декодирования.
И есть несколько API, которые делают этот же выбор по умолчанию.
Таким образом, кодировка по умолчанию приобретает важность, которую мы не можем безопасно игнорировать.
6.3. Проблемы С Набором Символов По Умолчанию
Как мы уже видели, кодировка по умолчанию в Java определяется динамически при запуске JVM. Это делает платформу менее надежной или подверженной ошибкам при использовании в разных операционных системах.
Например, если мы запустим
в macOS он будет использовать UTF-8.
Если мы попробуем тот же фрагмент кода в Windows, он будет использовать Windows-1252 для декодирования того же текста.
Или представьте, что вы пишете файл в mac OS, а затем читаете тот же файл в Windows.
Нетрудно понять, что из-за различных схем кодирования это может привести к потере или повреждению данных.
6.4. Можем ли мы переопределить кодировку по умолчанию?
Определение кодировки по умолчанию в Java приводит к двум системным свойствам:
Теперь интуитивно понятно переопределять эти системные свойства с помощью аргументов командной строки:
6.5. Почему Java Не Решает Эту Проблему?
Существует предложение по улучшению Java (JEP), которое предписывает использовать “UTF-8” в качестве кодировки по умолчанию в Java вместо того, чтобы основывать ее на кодировке локали и операционной системы.
Этот ДЖИП находится в состоянии проекта на данный момент и когда он (надеюсь!) пройдя через него, мы решим большинство вопросов, которые мы обсуждали ранее.
Обратите внимание, что более новые API, такие как в файле java.nio.file.Файлы не используют кодировку по умолчанию. Методы в этих API-интерфейсах читают или записывают символьные потоки с кодировкой UTF-8, а не с кодировкой по умолчанию.
6.6. Решение Этой Проблемы в Наших Программах
К счастью, наш пример уже определяет кодировку. Нам просто нужно выбрать правильный, и пусть Java сделает все остальное.
К настоящему времени мы должны понять, что акцентированные символы, такие как “ç”, отсутствуют в схеме кодирования ASCII, и поэтому нам нужна кодировка, которая включает их. Может быть, UTF-8?
Давайте попробуем это сделать, теперь мы запустим метод decode Text с тем же вводом, но с кодировкой “UTF-8”:
Бинго! Мы можем увидеть результат, который мы надеялись увидеть.
Аналогично, OutputStreamWriter и многие другие API поддерживают настройку схемы кодирования через свой конструктор.
6.7. Исключение MalformedInputException
Существует три предопределенные стратегии (или CodingErrorAction ), когда входная последовательность имеет искаженные входные данные:
По умолчанию malformedInputAction для кодера CharsetDecoder является REPORT, и по умолчанию malformedInputAction декодера по умолчанию в InputStreamReader is REPLACE.
Для третьего теста мы используем CodingErrorAction.ОТЧЕТ который приводит к выбрасыванию MalformedInputException:
7. Другие Места, Где Кодирование Важно
Нам не просто нужно учитывать кодировку символов при программировании. Тексты могут окончательно испортиться во многих других местах.
Давайте быстро рассмотрим несколько мест, где мы можем столкнуться с проблемами при кодировании или декодировании текста.
7.1. Текстовые Редакторы
В большинстве случаев текстовый редактор-это место, откуда исходят тексты. Существует множество текстовых редакторов в популярном выборе, включая vi, Блокнот и MS Word. Большинство из этих текстовых редакторов позволяют нам выбрать схему кодирования. Следовательно, мы всегда должны быть уверены, что они подходят для текста, с которым мы работаем.
7.2. Файловая система
После того, как мы создадим тексты в редакторе, нам нужно сохранить их в какой-то файловой системе. Файловая система зависит от операционной системы, на которой она работает. Большинство операционных систем имеют встроенную поддержку нескольких схем кодирования. Однако все еще могут быть случаи, когда преобразование кодировки приводит к потере данных.
7.3. Сеть
Тексты, передаваемые по сети с использованием протокола, такого как протокол передачи файлов (FTP), также включают преобразование между кодировками символов. Для всего, что закодировано в Юникоде, безопаснее всего передавать в двоичном виде, чтобы свести к минимуму риск потери при преобразовании. Однако передача текста по сети является одной из менее частых причин повреждения данных.
7.4. Базы данных
Большинство популярных баз данных, таких как Oracle и MySQL, поддерживают выбор схемы кодирования символов при установке или создании баз данных. Мы должны выбрать это в соответствии с текстами, которые мы ожидаем сохранить в базе данных. Это одно из наиболее частых мест, где повреждение текстовых данных происходит из-за преобразования кодировки.
7.5. Браузеры
Наконец, в большинстве веб-приложений мы создаем тексты и пропускаем их через различные слои с намерением просмотреть их в пользовательском интерфейсе, например в браузере. Здесь также важно, чтобы мы выбрали правильную кодировку символов, которая может правильно отображать символы. Большинство популярных браузеров, таких как Chrome, Edge, позволяют выбирать кодировку символов в своих настройках.
8. Заключение
В этой статье мы обсудили, как кодирование может быть проблемой при программировании.
Далее мы обсудили основные принципы, включая кодировку и кодировки. Более того, мы прошли через различные схемы кодирования и их использование.
Мы также подобрали пример неправильного использования кодировки символов в Java и увидели, как это сделать правильно. Наконец, мы обсудили некоторые другие распространенные сценарии ошибок, связанные с кодировкой символов.
Java: Юникод
Данные программы хранятся в памяти компьютера (оперативной или постоянной) в виде последовательности нулей и единиц. На этом уровне нет разницы между строками, числами или булевыми значениями, в памяти все выглядит одинаково. Разница появляется только в результате интерпретации. Программа знает, что внутри некоторой переменной хранится строка, поэтому она берет нули и единички и пропускает их сквозь кодовую таблицу, в которой указано, какому числу соответствует какая буква. В результате программист видит строку.
Поначалу все было хорошо, но с распространением компьютеров возникла потребность в других алфавитах. Каждая страна решала данную проблему созданием собственной кодировки, большинство из которых совместимы с ASCII. То есть первые 128 номеров полностью соответствовали ASCII, а вот остальные 128 заполнялись локальным алфавитом. 128 + 128 = 256, а это 2 в 8 степени. Эти кодировки были однобайтовыми (для хранения одного символа требовался один байт). Внезапно открылись врата ада. Попытка открыть в редакторе файл в другой кодировке, приводила к появлению крякозябр: Øèðîêàÿ ýëåêòðèôèêàöèÿ þæíûõ ãóáåðíèé äàñò ìîùíûé òîë÷îê ïîäú¸ìó ñåëüñêîãî õîçÿéñòâà. Возникают они потому, что один и тот же код в разных кодировках соответствует совершенно разным символам, за исключением первых 128. Поэтому текст, использующий английские буквы всегда читался, а в остальном как повезет. Ситуация усугублялась тем, что даже в рамках одного алфавита создавалось множество разных кодировок, например: Windows-1252, KOI8-R, CP 866, ISO 8859-5.
В языках программирования на тот момент все функции для работы со строками создавались из расчета, что один символ — это один байт. По крайней мере, это свойство было общим для всех кодировок.
Разные кодировки стали причиной постоянных проблем при взаимодействии людей и программ. Особенно остро эта проблема проявилась с развитием интернета. Такая ситуация не могла продолжаться бесконечно, и в конце концов был создан стандарт Юникод (Unicode). На данный момент он содержит более 100 тысяч символов и включает в себя все существующие (и даже мёртвые) языки. Стандарт Юникод не является кодировкой и ничего не говорит о том, как должны храниться символы в памяти, он лишь определяет связь между символом и некоторым числом. Конкретный способ кодирования юникода определяется соответствующими кодировками, среди которых UTF-8, UTF-16 и некоторые другие. В этих кодировках для хранения одного символа уже недостаточно одного байта, они используют больше. UTF-8 ведет себя хитрее: для символов английского алфавита (и некоторых других) используется один байт, для других алфавитов — 2.
После многих лет популяризации Юникода свершилось чудо, и сейчас подавляющее большинство программного обеспечения использует UTF-8. Этот процесс был болезненный и по-разному отразился на языках программирования.
Языки разделились на два лагеря. Некоторые встроили поддержку в уже существующие функции и переход на UTF-8 никак не отразился на процессе программирования. Среди них Java, Ruby, JavaScript.
Задание
Эта задача для тренировки и не относится к теме урока.