Provide feedback
Saved searches
Use saved searches to filter your results more quickly
Sign up
Appearance settings
- IT
- Cancel
Многие владельцы сайтов, в частности работающих на блоговом движке WordPress, редактируют PHP-файлы в блокноте, который прилагается к ОС Windows.
Поскольку сам движок WordPress работает с кодировкой UTF-8, а файлы шаблона, как правило, содержат русские буквы, т.е. кириллицу, то для правильного ее отображения такие файлы также необходимо сохранять в кодировке UTF-8.
Однако блокнот Windows при сохранении в кодировке UTF-8 добавляет в PHP-файлы невидимые символы, называемые BOM (кстати говоря, невидимые они именно в блокноте Винды), которые в результате приводят к тому, что либо на сайте отображается какая-то PHP-ошибка, либо “съезжает” дизайн.
Поэтому решение проблемы очень простое — при работе с кодировкой UTF-8 необходимо вообще забыть, что существует блокнот Windows и вместо него использовать нормальный текстовый редактор, который прекрасно умеет это делать. Например, Notepad++.
После установки Notepad++ откройте в нем целевой PHP-файл и меню выберите “Кодировки -> Кодировать в UTF-8 (без BOM)“:
После этого сохраните файл и загрузите его на свой сайт через FTP.
Если вы не знаете, какой из файлов сохранен в неправильной UTF-8 кодировке, то воспользуйтесь специальным скриптом, который найдет все файлы с BOM, которые необходимо пересохранить вышеописанным способом.
Все, на этом задачу с кодировкой можно считать решенной.
http://dimox.name/utf-8-without-bom/
Понравилась статья. Простенько и со вкусом. Буду пользоваться и другим рекомендую.
Только не забывайте, что если вы уже видите крикозябли в виде черных квадратиков, нужно в начале выбрать пункт «Преобразовать в UTF-8 без ВОМ». Вот так:
Вот теперь порядок.
From Wikipedia, the free encyclopedia
The byte-order mark (BOM) is a particular usage of the special Unicode character code, U+FEFF ZERO WIDTH NO-BREAK SPACE, whose appearance as a magic number at the start of a text stream can signal several things to a program reading the text:[1]
- the byte order, or endianness, of the text stream in the cases of 16-bit and 32-bit encodings;
- the fact that the text stream’s encoding is Unicode, to a high level of confidence;
- which Unicode character encoding is used.
BOM use is optional. Its presence interferes with the use of UTF-8 by software that does not expect non-ASCII bytes at the start of a file but that could otherwise handle the text stream.
Unicode can be encoded in units of 8-bit, 16-bit, or 32-bit integers. For the 16- and 32-bit representations, a computer receiving text from arbitrary sources needs to know which byte order the integers are encoded in. The BOM is encoded in the same scheme as the rest of the document and becomes a noncharacter Unicode code point if its bytes are swapped. Hence, the process accessing the text can examine these first few bytes to determine the endianness, without requiring some contract or metadata outside of the text stream itself. Generally the receiving computer will swap the bytes to its own endianness, if necessary, and would no longer need the BOM for processing.
The byte sequence of the BOM differs per Unicode encoding (including ones outside the Unicode standard such as UTF-7, see table below), and none of the sequences is likely to appear at the start of text streams stored in other encodings. Therefore, placing an encoded BOM at the start of a text stream can indicate that the text is Unicode and identify the encoding scheme used. This use of the BOM is called a «Unicode signature».[2]
The BOM is, simply, the Unicode codepoint U+FEFF ZERO WIDTH NO-BREAK SPACE, encoded in the current encoding. A text file beginning with the bytes FE FF suggests that the file is encoded in big-endian UTF-16.
The name ZWNBSP should be used if the BOM appears in the middle of a data stream. Unicode says it should be interpreted as a normal codepoint (namely a word joiner), not as a BOM. Since Unicode 3.2, this usage has been deprecated in favor of U+2060 WORD JOINER.[1]
The Unicode 1.0 name for this codepoint is also BYTE ORDER MARK.[3]
The UTF-8 representation of the BOM is the (hexadecimal) byte sequence EF BB BF.
The Unicode Standard permits the BOM in UTF-8,[4] but does not require or recommend its use.[5] UTF-8 always has the same byte order,[6] so its only use in UTF-8 is to signal at the start that the text stream is encoded in UTF-8, or that it was converted to UTF-8 from a stream that contained an optional BOM. The standard also does not recommend removing a BOM when it is there, so that round-tripping between encodings does not lose information, and so that code that relies on it continues to work.[7][8] The IETF recommends that if a protocol either (a) always uses UTF-8, or (b) has some other way to indicate what encoding is being used, then it «SHOULD forbid use of U+FEFF as a signature.»[9] An example of not following this recommendation is the IETF Syslog protocol which requires text to be in UTF-8 and also requires the BOM.[10]
Not using a BOM allows text to be backwards-compatible with software designed for extended ASCII. For instance many programming languages permit non-ASCII bytes in string literals but not at the start of the file.
A BOM is unnecessary for detecting UTF-8 encoding.[citation needed] UTF-8 is a sparse encoding: a large fraction of possible byte combinations do not result in valid UTF-8 text. Binary data and text in any other encoding are likely to contain byte sequences that are invalid as UTF-8, so existence of such invalid sequences indicates the file is not UTF-8, while lack of invalid sequences is a very strong indication the text is UTF-8. Practically the only exception is text containing only ASCII-range bytes, as this may be a non-ASCII 7-bit encoding, but this is unlikely in any modern data and even then the difference from ASCII is minor (such as changing ‘\’ to ‘¥’).
Microsoft compilers[11] and interpreters, and many pieces of software on Microsoft Windows such as Notepad (prior to Windows 10 Build 1903[12]) treat the BOM as a required magic number rather than use heuristics. These tools add a BOM when saving text as UTF-8, and cannot interpret UTF-8 unless the BOM is present or the file contains only ASCII. Windows PowerShell (up to 5.1) will add a BOM when it saves UTF-8 XML documents. However, PowerShell Core 6 has added a -Encoding switch on some cmdlets called utf8NoBOM so that document can be saved without BOM. Google Docs also adds a BOM when converting a document to a plain text file for download.
In UTF-16, a BOM (U+FEFF) may be placed as the first bytes of a file or character stream to indicate the endianness (byte order) of all the 16-bit code units of the file or stream. If an attempt is made to read this stream with the wrong endianness, the bytes will be swapped, thus delivering the character U+FFFE, which is defined by Unicode as a «noncharacter» that should never appear in the text.
- If the 16-bit units are represented in big-endian byte order («UTF-16BE»), the BOM is the (hexadecimal) byte sequence
FE FF - If the 16-bit units use little-endian order («UTF-16LE»), the BOM is the (hexadecimal) byte sequence
FF FE
For the IANA registered charsets UTF-16BE and UTF-16LE, a byte-order mark should not be used because the names of these character sets already determine the byte order.
If there is no BOM, it is possible to guess whether the text is UTF-16 and its byte order by searching for ASCII characters (i.e. a 0 byte adjacent to a byte in the 0x20-0x7E range, also 0x0A and 0x0D for CR and LF). A large number (i.e. far higher than random chance) in the same order is a very good indication of UTF-16 and whether the 0 is in the even or odd bytes indicates the byte order. However, this can result in both false positives and false negatives.
Clause D98 of conformance (section 3.10) of the Unicode standard states, «The UTF-16 encoding scheme may or may not begin with a BOM. However, when there is no BOM, and in the absence of a higher-level protocol, the byte order of the UTF-16 encoding scheme is big-endian.» Whether or not a higher-level protocol is in force is open to interpretation. Files local to a computer for which the native byte ordering is little-endian, for example, might be argued to be encoded as UTF-16LE implicitly. Therefore, the presumption of big-endian is widely ignored. The W3C/WHATWG encoding standard used in HTML5 specifies that content labelled either «utf-16» or «utf-16le» are to be interpreted as little-endian «to deal with deployed content».[13] However, if a byte-order mark is present, then that BOM is to be treated as «more authoritative than anything else».[14]
Although a BOM could be used with UTF-32, this encoding is rarely used for transmission. Otherwise the same rules as for UTF-16 are applicable.
The BOM for little-endian UTF-32 is the same pattern as a little-endian UTF-16 BOM followed by a UTF-16 NUL character, an unusual example of the BOM being the same pattern in two different encodings. Programmers using the BOM to identify the encoding will have to decide whether UTF-32 or UTF-16 with a NUL first character is more likely.
Byte-order marks by encoding
[edit]
This table illustrates how the BOM is represented as a byte sequence in various encodings and how those sequences might appear in a text editor that is interpreting each byte as a legacy encoding (Windows-1252 and caret notation for the C0 controls):
| Encoding | Representation (hexadecimal) | Representation (decimal) | Bytes interpreted as Windows-1252 |
|---|---|---|---|
| UTF-8[a] | EF BB BF | 239 187 191 |  |
| UTF-16 (BE) | FE FF | 254 255 | þÿ |
| UTF-16 (LE) | FF FE | 255 254 | ÿþ |
| UTF-32 (BE) | 00 00 FE FF | 0 0 254 255 | ^@^@þÿ (^@ is the null character) |
| UTF-32 (LE) | FF FE 00 00 | 255 254 0 0 | ÿþ^@^@ (^@ is the null character) |
| UTF-7[a] | 2B 2F 76[b][16][17] | 43 47 118 | +/v |
| UTF-1[a] | F7 64 4C | 247 100 76 | ÷dL |
| UTF-EBCDIC[a] | DD 73 66 73 | 221 115 102 115 | Ýsfs |
| SCSU[a] | 0E FE FF[c] | 14 254 255 | ^Nþÿ (^N is the «shift out» character) |
| BOCU-1[a] | FB EE 28 | 251 238 40 | ûî( |
| GB18030[a] | 84 31 95 33 | 132 49 149 51 | „1•3 |
- ^ a b c d e f g This is not literally a «byte order» mark, since a code unit in these encodings is one byte and therefore cannot have bytes in a «wrong» order. Nevertheless, the BOM can be used to indicate the encoding of the text that follows it.[6][15]
- ^ Followed by
38,39,2B, or2F(ASCII8,9,+or/), depending on what the next character is. - ^ SCSU allows other encodings of U+FEFF, the shown form is the signature recommended in UTR #6.[18]
- Left-to-right mark
- Arabic Presentation Forms-B, block to which code point
U+FEFFbelongs
- ^ a b «FAQ — UTF-8, UTF-16, UTF-32 & BOM». Unicode.org. Retrieved 28 January 2017.
- ^ «The Unicode® Standard Version 9.0» (PDF). The Unicode Consortium.
- ^ «Zero Width No-Break Space (U+Feff)».
- ^ «The Unicode Standard 5.0, Chapter 2:General Structure» (PDF). p. 36. Retrieved 29 March 2009.
Table 2-4. The Seven Unicode Encoding Schemes
- ^ «The Unicode Standard 5.0, Chapter 2:General Structure» (PDF). p. 36. Retrieved 30 November 2008.
Use of a BOM is neither required nor recommended for UTF-8, but may be encountered in contexts where UTF-8 data is converted from other encoding forms that use a BOM or where the BOM is used as a UTF-8 signature
- ^ a b «FAQ — UTF-8, UTF-16, UTF-32 & BOM: Can a UTF-8 data stream contain the BOM character (in UTF-8 form)? If yes, then can I still assume the remaining UTF-8 bytes are in big-endian order?». Unicode.org. Retrieved 4 January 2009.
- ^ «Re: pre-HTML5 and the BOM from Asmus Freytag on 2012-07-13 (Unicode Mail List Archive)». Unicode.org. Retrieved 14 July 2012.
- ^ «Bug ID: JDK-6378911 UTF-8 decoder handling of byte-order mark has changed». Bugs.java.com. Retrieved 14 October 2021.
- ^ Yergeau, Francois (November 2003). UTF-8, a transformation format of ISO 10646. IETF. doi:10.17487/RFC3629. RFC 3629. Retrieved 15 May 2014.
- ^ Gerhards, Rainer (March 2009). «MSG». The Syslog Protocol. IETF. sec. 6.4. doi:10.17487/RFC5424. RFC 5424.
- ^ Alf P. Steinbach (2011). «Unicode part 1: Windows console i/o approaches». Retrieved 24 March 2012.
However, since the C++ source code was encoded as UTF-8 without BOM (as is usual in Linux), the Visual C++ compiler erroneously assumed that the source code was encoded as Windows ANSI.
- ^ «Windows 10 Notepad is Getting Better UTF-8 Encoding Support». BleepingComputer. Retrieved 7 March 2023.
- ^ «UTF-16LE». Encoding Standard. WHATWG.
- ^ «Decode». Encoding Standard. WHATWG.
- ^ Yergeau, François (8 November 2003). «RFC 3629 — UTF-8, a transformation format of ISO 10646». Ietf Datatracker. Retrieved 28 January 2017.
- ^ Honermann, Tom (2 January 2021). «Clarify guidance for use of a BOM as a UTF-8 encoding signature» (PDF). Unicode.
- ^ «SDL Documentation».
- ^ Markus Scherer. «UTS #6: Compression Scheme for Unicode». Unicode.org. Retrieved 28 January 2017.
- Unicode FAQ: UTF-8, UTF-16, UTF-32 & BOM
- The Unicode Standard, chapter 2.6 Encoding Schemes
- The Unicode Standard, chapter 2.13 Special Characters and Noncharacters, section Byte Order Mark (BOM)
- The Unicode Standard, chapter 16.8 Specials, section Byte Order Mark (BOM): U+FEFF
Уровень сложностиСредний
Время на прочтение13 мин
Количество просмотров28K
Мне понадобилось провести несколько вводных уроков по языку программирования C++. В интернете есть много разнообразных учебных пособий для начинающих. Но почти во всех из них символьные и строковые литералы в примерах и упражнениях даются на английском языке, начиная со знаменитой первой программы «Hello, world!».
Насколько я понимаю, действующий стандарт языка C++ (ISO/IEC 14882:2020) определяет для исходного кода (текста) программы базовый набор возможных символов (basic source character set) в количестве 96 штук (в том числе буквы латиницы), в который можно добавлять дополнительные символы из набора символов Юникода с помощью специальной нотации (universal character name). Например, символ ? (U+1F60E) не входит в 96 символов базового набора, но я могу его добавить в исходный код программы с помощью последовательности \U0001F60E.
Таким образом, по идее, ничего не мешает нам использовать в исходном коде программы буквы русского алфавита или буквы алфавитов других языков. Почему авторы руководств для начинающих (в том числе — большинства русскоязычных) по языку C++ этого не делают — для меня загадка. Я пытался задать этот вопрос на известном сайте вопросов и ответов по программированию «Stack Overflow», но мой вопрос удалили, так как сайт «Stack Overflow» не принимает вопросы, на которые нельзя дать четкий ответ. То есть вопросы, на которые можно ответить с разных точек зрения по-разному, вызывающие дискуссии, там запрещены.
Всё же до удаления моего вопроса я успел получить несколько мнений по этому поводу. Цитата 1: «Because for beginners they have zero relevance. What would outputting Привет, мир instead of Hello, world add to the knowledge of C++?» Я думаю, что, как минимум, русскоязычному ученику было бы удобнее и интереснее (это важно!) писать первые программы, выводящие сообщения на русском языке и, к примеру, с эмодзи. Особенно, если речь идет об обучении детей. При этом я не имею ничего против английского языка, я люблю английский язык, на нем написано много полезной литературы по программированию.
Цитата 2: «because dealing with character encodings is an intermediate/advanced/complex topic, not well-suited for beginners. They have enough to deal with just focusing on the complexities of the standard language and its base features». Тут я соглашусь только частично. На мой взгляд, язык C++ — это изначально язык программирования, приближенный к «железу». Некоторые авторы даже относят C++ не к высокоуровневым языкам, а к языкам среднего уровня. То есть, по моему мнению, ученик изначально должен получить представление о хранении данных, особенно текстов, на компьютере. Текст (в том числе исходный код программы) всегда хранится в какой-то кодировке. Поэтому обучение языку C++ должно начинаться с кодировок текста, или хотя бы с одной кодировки, UTF-8.
Файлы с исходным кодом, кодировка и другие особенности
На сегодняшний день для текстовых файлов стандартом по факту стала кодировка UTF-8, одна из реализаций таблицы Юникода. Мне кажется очевидным, что начинающих изучать язык C++ следует учить писать тексты программ в этой кодировке, независимо от операционной системы. По этой теме есть отличный манифест — «UTF-8 Everywhere».
Еще при написании исходного кода примеров и упражнений для начинающих, думаю, имеет смысл стараться сохранять кроссплатформенность исходного кода, хотя бы для операционных систем «Windows» и «Linux». Современные редакторы кода и компиляторы облегчают эту задачу: они умеют работать с окончаниями строк разных видов (CRLF или LF), с кодировкой UTF-8 (с меткой BOM или без нее) и так далее.
Компилятор и другие инструменты
В принципе, для начального обучения языку C++ можно использовать веб-компиляторы, для работы с которыми нужен только доступ в интернет и браузер. Их довольно много, я приведу для примера несколько первых попавшихся мне в поисковой системе: OnlineGDB.com, Cpp.sh, Online-Cpp.com и так далее.
Однако, я хочу, чтобы сразу была видна работа с компилятором, компоновщиком, их ключами, как из интегрированной среды разработки, так и из командной строки. По идее, нужно сразу дать понять, что язык C++ — это компилируемый язык программирования. Поэтому мне удобнее для обучения использовать программы-инструменты на настольном компьютере, а не веб-приложения.
Вообще, хотелось бы размахнуться на работу сразу в двух операционных системах — «Windows 10» и каком-нибудь дистрибутиве «Linux» на отдельных компьютерах, но бюджет пока не позволяет, поэтому ограничиваюсь только операционной системой «Windows 10» с возможным использованием подсистемы «WSL» вместо «Linux».
Есть несколько хороших компиляторов, но я использую MSVC (Microsoft Visual C++) самой свежей версии в составе набора инструментов командной строки «Microsoft C++ Build Tools». Это тот же компилятор, который использует интегрированная среда разработки «Microsoft Visual Studio 2022», которую я тоже установил к себе на компьютер в виде бесплатной версии под названием «Visual Studio Community 2022». В дистрибутиве «Ubuntu» из семейства операционных систем «Linux» (через WSL) я использую компилятор C++ из набора компиляторов «GCC».
Интегрированную среду разработки я установил, только чтобы показать работу из нее. Вообще она очень неповоротливая и тяжелая, у меня еле ворочается. Для ученических упражнений я установил редактор «Visual Studio Code», который можно настроить для работы с набором инструментов «Microsoft C++ Build Tools» (компиляция, пошаговая отладка и другие удобные функции).
Для работы из командной строки в большинстве случаев я использую программу-оболочку «PowerShell» и программу-«эмулятор терминала» «Windows Terminal». (Для работы с компилятором MSVC из командной строки приходится использовать программу-оболочку «Developer PowerShell for VS 2022», построенную на основе устаревающей программы-оболочки «Windows PowerShell» версии 5.1.) В дистрибутиве «Ubuntu» операционной системы «Linux» я использую программу-оболочку «bash» (она там по умолчанию).
Первая программа с символами не из базового набора
Для этой статьи я буду использовать следующий исходный код первой программы на языке C++, который сохраню в файле «first.cpp» в кодировке UTF-8 без метки BOM, с окончаниями строк CRLF (хотя ничего мне не мешает использовать кодировку UTF-8 с меткой BOM и окончания строк LF):
#include <iostream>
int main()
{
std::cout << "Hello, World! Привет, мир! 你好, 世界! ?\n";
return 0;
}
Как видно из блока кода выше, эта программа содержит множество символов, выходящих за пределы базового набора возможных символов (буквы русского алфавита, китайские иероглифы, эмодзи) и я не стал представлять их специальной нотацией (universal character name) вроде \U0001F60E, как того требует действующий стандарт языка C++.
Современные компиляторы (вроде MSVC и других) расширяют действующий стандарт C++, позволяя в качестве базового набора возможных символов использовать все символы, входящие в набор символов используемой кодировки, то есть в данном случае это все символы набора символов Юникода. Об этом сказано в документации на сайте компании Microsoft (при описании языка C++ там расширения, вводимые компанией Microsoft, помечены фразой «Microsoft Specific»).
Я думаю, что это расширение настолько очевидно необходимо, что его должны рано или поздно включить в состав стандарта языка C++.
Метка BOM, для чего она нужна, использовать ли её
Если все люди будут использовать для текстовых файлов кодировку UTF-8 по умолчанию, то метка BOM (Byte Order Mark) будет не нужна. Но мы живем не в идеальном мире. Если говорить об операционных системах «Windows», то их разработчики хотят пока что сохранять обратную совместимость с устаревшими однобайтными кодировками вроде Windows-1251, CP866 и так далее. Поэтому в операционных системах «Windows» для того, чтобы программы могли автоматически распознать (отличить от устаревших однобайтных) кодировку UTF-8, в начало файла добавляют так называемую «метку BOM», которая для кодировки UTF-8 представляет собой три байта EF BB BF.
Таким образом, кодировки «UTF-8 без BOM» и «UTF-8 с BOM» не являются разными кодировками, это одна и та же кодировка, но в случае последней длина файла с исходным кодом просто удлиняется на 3 байта за счет вставки метки BOM в начало файла.
Используемые мною интегрированная среда разработки «Visual Studio Community», редактор кода «Visual Studio Code» и редактор кода «Notepad++» умеют работать как с кодировкой «UTF-8 без BOM» (или просто «UTF-8»), так и с кодировкой «UTF-8 с BOM». Вообще, на мой взгляд, современная программа, имеющая дело с текстом, должна уметь обработать присутствие в файле метки BOM.
Решение о том, какой из двух вариантов «UTF-8 без BOM» и «UTF-8 с BOM» использовать, на мой взгляд, должен принимать программист (или группа программистов, если речь идет о выработке руководства по стилю в случае совместной разработки) и это должно зависеть от его соображений, а не от каких-то внешних рекомендаций или требований. Таким образом, хорошие программы-инструменты должны обеспечивать работу как с «UTF-8 без BOM», так и с «UTF-8 с BOM».
Например, компилятор MSVC умеет работать как с исходным кодом в кодировке «UTF-8 без BOM», так и с исходным кодом в кодировке «UTF-8 с BOM». Если исходный код хранится в кодировке «UTF-8 с BOM», то компилятор MSVC распознает кодировку UTF-8 по умолчанию. Если исходный код хранится в кодировке «UTF-8 без BOM», то компилятор MSVC по умолчанию не сможет распознать кодировку UTF-8, поэтому потребуется использование ключа компилятора /utf-8 (тут подробнее). Ниже я приведу два простейших примера запуска компилятора MSVC (cl.exe) из командной строки для этих двух вариантов.
Если файл «first.cpp» содержит исходный код программы в кодировке UTF-8 с меткой BOM:
PS C:\test> cl /EHsc "first.cpp"
Если файл «first.cpp» содержит исходный код программы в кодировке UTF-8 без метки BOM:
PS C:\test> cl /EHsc /utf-8 "first.cpp"
Ключ компилятора /EHsc (тут подробнее) определяет модель обработки ошибок. Это рекомендуемый ключ для начинающих. Его обсуждение выходит за рамки темы, заявленной в этой статье, поэтому я не буду тут про него писать.
Запуск программы «first.cpp» в веб-компиляторах
Некоторые веб-компиляторы успешно справляются с компиляцией приведенного выше исходного кода программы «first.cpp» в кодировке UTF-8, но некоторые могут не справиться и выдать результат, не соответствующий ожидаемому. Как я писал выше, я пока не собираюсь использовать веб-компиляторы, но, думаю, не повредит привести работающий пример. Вот иллюстрация успешной компиляции и запуска исходного кода программы «first.cpp» в кодировке UTF-8 в веб-компиляторе «OnlineGDB.com»:
Запуск программы «first.cpp» в системе «Windows 10»
В операционных системах «Windows» приходится учитывать стремление разработчиков этой операционной системы обеспечивать по умолчанию работу устаревших программ, использующих устаревшие однобайтные кодировки вроде Windows-1251, CP866 и так далее. Я об этом уже упоминал ранее в этой статье.
Это стремление выражается в том, что программы-инструменты, предназначенные для эмуляции терминала (консоли), по умолчанию настроены для работы с одной из устаревших однобайтных кодировок. Какая конкретно это однобайтная кодировка, зависит от текущей локали (языка системы, по английски «system locale») операционной системы (не путать с языком интерфейса операционной системы). Например, у меня язык системы — русский, для этого случая в эмуляторе терминала (консоли) в операционных системах «Windows» по умолчанию используется кодировка CP866.
Что тогда делать, если мы хотим использовать кодировку UTF-8? Я пользуюсь в разных случаях тремя способами для решения этой проблемы. Далее в примерах я буду использовать файл «first.cpp» с исходным кодом в кодировке UTF-8 (без метки BOM).
Способ 1. Самый легкий, сохраняющий кроссплатформенность исходного кода. Этот способ подразумевает предварительное переключение активной кодовой страницы в используемой программе-оболочке. Думаю, его и следует использовать при обучении языку C++.
(Кроме переключения активной кодовой страницы программы-оболочки для правильного отображения символов из набора символов Юникода требуется наличие шрифта, который содержал бы изображения всех нужных символов, так как почти все шрифты содержат только ограниченное количество символов из таблицы Юникода. Символов в таблице Юникода слишком много, чтобы в одном шрифте реализовали их все. Современные эмуляторы терминалов, кстати, умеют исполнять так называемый «font fallback», отображая отсутствующие символы другим шрифтом, в котором нужные символы есть. Впрочем, в операционной системе «Windows 10» при использовании современных программ проблем со шрифтами у меня почти не возникает. То есть специальных действий по настройке шрифта обычно не требуется.)
1.1. Компиляция из программы-оболочки «Developer PowerShell for VS 2022»:
**********************************************************************
** Visual Studio 2022 Developer PowerShell v17.5.3
** Copyright (c) 2022 Microsoft Corporation
**********************************************************************
PS C:\test> cl /EHsc /utf-8 "first.cpp"
Оптимизирующий компилятор Microsoft (R) C/C++ версии 19.35.32216.1 для x64
(C) Корпорация Майкрософт (Microsoft Corporation). Все права защищены.
first.cpp
Microsoft (R) Incremental Linker Version 14.35.32216.1
Copyright (C) Microsoft Corporation. All rights reserved.
/out:first.exe
first.obj
1.2.а. Запуск полученного исполняемого файла из программы-оболочки «cmd.exe»:
Microsoft Windows [Version 10.0.19045.2846]
(c) Корпорация Майкрософт (Microsoft Corporation). Все права защищены.
C:\test>chcp
Текущая кодовая страница: 866
C:\test>chcp 65001
Active code page: 65001
C:\test>first
Hello, World! Привет, мир! 你好, 世界! ?
Иллюстрация:
Команда chcp (тут подробнее) без параметров показывает номер активной кодовой страницы. Как я и писал выше, по умолчанию в программе-оболочке у меня включается кодовая страница с номером 866. Для работы с текстом в кодировке UTF-8 требуется кодовая страница с номером 65001.
1.2.б. Запуск полученного исполняемого файла из программы-оболочки «PowerShell» (версия 7) или «Windows PowerShell» (версия 5.1):
PS C:\test> ([System.Console]::OutputEncoding).CodePage
866
PS C:\test> [System.Console]::OutputEncoding = [System.Text.Encoding]::UTF8
PS C:\test> ([System.Console]::OutputEncoding).CodePage
65001
PS C:\test> .\first
Hello, World! Привет, мир! 你好, 世界! ?
Иллюстрация:
Обратите внимание, что в программах-оболочках «PowerShell» и «Windows PowerShell» команда chcp может не срабатывать (рапортует, что активная кодовая страница переключена, а на самом деле переключения не происходит). Для программ-оболочек «PowerShell» для переключения активной кодовой страницы требуется изменение свойства «OutputEncoding» класса «System.Console» (платформа «.NET»). См. обсуждение этого вопроса на сайте «Stack Overflow».
Программное переключение активной кодовой страницы консоли в «Windows 10»
Исходя из вышесказанного, я хочу подытожить, что сложность использования кодировки UTF-8 в консольных программах в операционных системах «Windows» порождается не стандартом языка C++ и не компилятором (в нашем случае это MSVC), а тем, что активной кодовой страницей в консолях является не UTF-8 (как хотелось бы), а одна из устаревших однобайтовых кодировок, вроде CP866 в моем случае. Мне кажется, в будущем разработчики операционных систем «Windows» рано или поздно это исправят. Наверное, это зависит от того, насколько быстро пользователи будут отказываться от устаревшего программного обеспечения, использующего устаревшие однобайтные кодировки, и переходить на программное обеспечение, использующее кодировку UTF-8 для текстов.
Я в разных случаях использую два способа переключения активной кодовой страницы консоли из программы. Тут следует иметь в виду, что оба эти способа используют специфические для операционной системы «Windows» функции и поэтому их использование делает программу не кроссплатформенной. Из-за этого эти способы кажутся мне неудобными и не совсем подходящими для обучения языку C++.
С другой стороны, у этих способов есть преимущество перед описанным выше: от пользователя полученного исполняемого файла не потребуется предварительная настройка (переключение активной кодовой страницы) используемой им программы-оболочки. Пользователю достаточно будет просто запустить полученный исполняемый файл.
Способ 2. Продолжаем использовать «узкие» версии функций для работы с символами. Для переключения активной кодовой страницы используем функцию «SetConsoleOutputCP» из набора функций «Windows API» (то есть понадобится подключение заголовочного файла «windows.h»). Изменим исходный код:
#include <windows.h> // для функции SetConsoleOutputCP и константы CP_UTF8
#include <iostream>
int main()
{
SetConsoleOutputCP(CP_UTF8);
std::cout << "Hello, World! Привет, мир! 你好, 世界! ?\n";
return 0;
}
Обратите внимание, я лишь добавил в исходный код две новые строки, а строки предыдущей версии исходного кода не изменял.
Компилируем, запускаем. Теперь предварительная настройка активной кодовой страницы в консоли не требуется, программа работает так, как от нее ожидалось:
Способ 3. Если мы хотим использовать «широкие» версии функций для работы с символами, то для правильного вывода символов в консоль потребуется использовать функцию «_setmode» (требует подключения заголовочного файла «io.h»; если в коде используются соответствующие константы, то еще потребуется подключение заголовочного файла «fcntl.h»). Изменим исходный код:
#include <io.h> // для функции _setmode
#include <fcntl.h> // для константы _O_U8TEXT
#include <iostream>
int main()
{
_setmode(_fileno(stdout), _O_U8TEXT);
std::wcout << L"Hello, World! Привет, мир! 你好, 世界! ?\n";
return 0;
}
Обратите внимание, что я не только добавил три новые строки в первоначальную версию исходного кода из начала поста, но еще заменил функцию std::cout на std::wcout, а также сделал приставку «L» к строковому литералу. Эти два последних изменения нужны для работы с так называемыми «широкими» символами (тип «wchar_t»).
Компилируем, запускаем. Теперь предварительная настройка активной кодовой страницы в консоли не требуется (как и при использовании способа 2), программа работает почти так, как от нее ожидалось:
Обратите внимание на то, что символ эмодзи ? (U+1F60E) отобразился неверно. Это не ошибка программы-оболочки или программы-«эмулятора терминала». Также это не ошибка способа 3 в целом, но тут есть одна тонкость. Дело в том, что тип «wchar_t» («широкие» символы) по стандарту языка C++ является платформозависимым (поэтому многие программисты его недолюбливают). В операционных системах «Windows» «широкие» символы имеют фиксированный размер в 2 байта и, следовательно, могут представить только 65536 символов (2 в шестнадцатой степени). (В операционных системах «Linux», насколько я знаю, «широкие» символы имеют размер в 4 байта.)
Таким образом, «широкий» символ может представлять букву русского алфавита или китайский иероглиф, потому что в таблице Юникода коды русских букв и китайских иероглифов входят в состав первых 65536 позиций (основная многоязычная плоскость, по-английски «Basic Multilingual Plane» или сокращенно «BMP»). Как видно по коду символа эмодзи ? (U+1F60E), он не входит в эти 65536 позиций и поэтому «широкий» символ в операционных системах «Windows» не может хранить символ эмодзи ? (U+1F60E).
То есть ошибка — в 9 строке исходного кода, в которой определен строковый литерал. Строковый литерал с приставкой «L» не может содержать символ эмодзи ? (U+1F60E), как уже было объяснено выше. Насколько я понимаю, код символа ? (U+1F60E) обрезается так, чтобы символ влез в строку «широких» символов, в итоге получается не то, что ожидалось.
Если использовать способ 3 только для русских букв, китайских иероглифов и/или других символов, коды которых входят в основную многоязычную плоскость (BMP) Юникода, проблем не возникнет.
Запуск программы «first.cpp» в системе «Linux»
Вернемся к самому первому варианту исходного кода программы «first.cpp» из начала этой статьи. Я его сохранил в своей основной операционной системе «Windows 10» в местоположении «C:\Users\Илья\source\repos\test\first.cpp». Через подсистему «WSL 2» я могу работать с установленным у меня дистрибутивом «Ubuntu» операционной системы «Linux». В дистрибутиве «Ubuntu» я установил пакет «g++» с компилятором языка C++ из набора компиляторов «GCC». Запускаем компиляцию, а затем запускаем полученный исполняемый файл на выполнение:
ilya@IlyaComp:~/test$ g++ /mnt/c/Users/Илья/source/repos/test/first.cpp -o first
ilya@IlyaComp:~/test$ ls -l
total 16
-rwxr-xr-x 1 ilya ilya 16376 Apr 22 18:35 first
ilya@IlyaComp:~/test$ ./first
Hello, World! Привет, мир! 你好, 世界! ?
Иллюстрация:
Как видно из примера выше, я использовал тот же файл, что и при работе с компилятором MSVC в операционной системе «Windows 10». То есть исходный код в файле «first.cpp» из начала статьи является кроссплатформенным для операционных систем «Windows» и «Linux». Конечно, исполняемые файлы получаются абсолютно разные: формата PE и формата ELF соответственно.
Работать с кодировкой UTF-8 в операционных системах «Linux» легче, чем в операционных системах «Windows», так как, насколько я понимаю, в операционных системах «Linux» кодировка UTF-8 используется по умолчанию.
Заключение
Главная цель этой статьи — показать, что обучать программированию на языке C++ можно, сразу начав работу в кодировке UTF-8. Думаю, что хороший учебник по программированию на языке C++, написанный на русском языке, должен в основном включать примеры и упражнения с символьными и строковыми литералами на русском языке. Пока я не видел хороших учебников, которые учитывали бы это соображение. Но, надеюсь, они вскоре появятся, как в бумажном виде, так и в виде сайта или веб-приложения.
Believe it or not, There is no such thing as Plain Text!
All files in a modern Operating Sytems (Windows, Linux, or MacOSX) are
saved with an encoding scheme! They are encoded (a table mapping of what
each byte means) in such way so that other programs can read it back
and understand how to get information out. It happens that US/ASCII
encoding is earliest and widely used that people think it’s just «Plain
Tex». But even ASCII is an encoding! It uses 7 bits in mapping all US
characters in saving the bytes into file. Obviously you are free to use
any kind of encoding (mapping) scheme to save any files, but if you want
other programs to read it back easily, then sticking to some standard
ones would help a lot. Without an agreed upon encoding, programs will
not able to read files and be any useful!
The most useful and practical file encoding today is «UTF-8» because it support Unicode, and it’s widely used in internet.
I discovered something odd when using Eclipse and Notepadd++. In
Ecilpse, if we set default encoding with UTF-8, it would use normal
UTF-8 without the Byte Order Mark (BOM). But in Notepad++, it appears to
support UTF-8 wihtout BOM, but it won’t recoginze it when first open.
You can check this by going Menu > Encoding and see which one is
selected. Notepad++ seems to only recognize UTF-8 wihtout BOM with ones
it converted by it’s own conversion utility. Perhaps it’s a bug in
notepad++.
So what is BOM? The byte order mark is
useless for UTF-8. They only used for UTF-16 so they know which byte
order is first. But UTF-8 will allow you to save these BOM for
conversion purpose… they are ineffective in encoding the doc itself.
So a «normal» UTF-8, it won’t have BOM, but Windows would like to use
them anyway. The Windows NOTEPAD would automatically save BOM in UTF-8!
So be-aware when viewing UTF-8 without BOM encoding files in Notepad++, as it can be deceiving at first glance.
