Что такое код is_string


Содержание

Что такое код is_string

В современном стандарте C++ определен класс с функциями и свойствами (переменными) для организации работы со строками (в классическом языке C строк как таковых нет, есть лишь массивы символов char ):

Для работы со строками также нужно подключить стандартный namespace :

В противном случае придётся везде указывать описатель класса std::string вместо string .

Ниже приводится пример программы, работающей со string (в старых си-совместимых компиляторах не работает!):

Основные возможности, которыми обладает класс string :

  • инициализация массивом символов (строкой встроенного типа) или другим объектом типа string . Встроенный тип не обладает второй возможностью;
  • копирование одной строки в другую. Для встроенного типа приходится использовать функцию strcpy() ;
  • доступ к отдельным символам строки для чтения и записи. Во встроенном массиве для этого применяется операция взятия индекса или косвенная адресация с помощью указателя;
  • сравнение двух строк на равенство. Для встроенного типа используются функции семейства strcmp() ;
  • конкатенация (сцепление) двух строк, дающая результат либо как третью строку, либо вместо одной из исходных. Для встроенного типа применяется функция strcat() , однако чтобы получить результат в новой строке, необходимо последовательно задействовать функции strcpy() и strcat() , а также позаботиться о выделении памяти;
  • встроенные средства определения длины строки (функции-члены класса size() и l ength() ). Узнать длину строки встроенного типа можно только вычислением с помощью функции strlen() ;
  • возможность узнать, пуста ли строка.

Рассмотрим эти базовые возможности более подробно.

Инициализация строк при описании и длина строки (не включая завершающий нуль-терминатор):

Строка может быть задана и пустой:

Для проверки того, пуста ли строка, можно сравнить ее длину с 0:

или применить метод empty() , возвращающий true для пустой строки и false для непустой:

Третья форма создания строки инициализирует объект типа string другим объектом того же типа:

Строка st3 инициализируется строкой st . Как мы можем убедиться, что эти строки совпадают? Воспользуемся оператором сравнения ( == ):

Как скопировать одну строку в другую? С помощью обычной операции присваивания:

Для сцепления строк используется операция сложения ( + ) или операция сложения с присваиванием ( += ). Пусть даны две строки:

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

Если же мы хотим добавить s2 в конец s1 , мы должны написать:

Операция сложения может сцеплять объекты класса string не только между собой, но и со строками встроенного типа. Можно переписать пример, приведенный выше, так, чтобы специальные символы и знаки препинания представлялись встроенным типом char * , а значимые слова – объектами класса string :

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

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

Чтобы осуществить такое преобразование, необходимо явно вызвать функцию-член с названием c_str() («строка Си»):

Функция c_str() возвращает указатель на символьный массив, содержащий строку объекта string в том виде, в каком она находилась бы во встроенном строковом типе. Ключевое слово const здесь предотвращает «опасную» в современных визуальных средах возможность непосредственной модификации содержимого объекта через указатель.

К отдельным символам объекта типа string , как и встроенного типа, можно обращаться с помощью операции взятия индекса. Вот, например, фрагмент кода, заменяющего все точки символами подчеркивания:

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

Правда, здесь использован не метод replace класса string , а одноимённый алгоритм:

Поскольку объект string ведет себя как контейнер, к нему могут применяться и другие алгоритмы. Это позволяет решать задачи, не решаемые напрямую функциями класса string .

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

Задание символов в строке

присваивает значения строке

назначает символы строке

Доступ к отдельным символам

получение указанного символа с проверкой выхода индекса за границы

получение указанного символа

получение первого символа

получение последнего символа

возвращает указатель на первый символ строки

возвращает немодифицируемый массив символов С , содержащий символы строки

Проверка на вместимость строки

проверяет, является ли строка пустой

возвращает количество символов в строке

возвращает максимальное количество символов

Что такое код is_string

Все уроки по C# расположены здесь.

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

В C# строковой тип данных (String) используется для определения ряда символов Unicode. Серия может быть как из нуля символов (пустая строка) или гораздо больше. Теоретический максимальный размер составляет несколько миллиардов символов. Однако это может быть недостижимо, так как операционная система может не предоставить достаточно памяти для хранения таких строк.

Строки — это объекты

Переменные типы в c# могут быть разделены на две группы. Эти типы значений и ссылочные типы. Все типы данных, которые были рассмотрены до сих пор в нашем учебнике — то были типы значений, или структур. Строковый тип данных — это не структура, это класс. Классы отличаются от структур. Они используются в объектно-ориентированном программировании – я писал уже о нем, но напишу и здесь некоторые ключевые моменты о классах, которые вы должны знать.

Все строки являются объектами типа string. Все объекты могут иметь значение null, указывающее, что они не определены. Поэтому любой объект string является nullable. В отличие от типов данных nullable numeric и character, описанных ранее в этом руководстве, это относится ко всем версиям платформы .NET framework.

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

Важным свойством строк в том, что они доступны только для чтения или неизменяемы. После создания строки ее содержимое не может быть изменено. Это может быть сюрпризом даже для опытных разработчиков C#, так как строки могут быть легко созданы и изменены. Однако при обновлении строки .NET framework фактически удаляет оригинал и создает новую строку. Для строк, которые изменяются нечасто, это позволяет их более эффективно обрабатывать. Часто модифицируемые строки могут быстро стать неэффективными; в этом случае лучше использовать другой класс StringBuilder.

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

Как указывалось ранее, строки являются объектами и поэтому могут содержать null. Значение null может быть присвоено аналогичным образом, как и при использовании nullable в числовых типов данных.

Строка null указывает, что переменная не определена. Часто требуется сохранить пустую строку. Это отличается от null, поскольку значение определено, хотя оно не содержит символов. Существует два способа присвоения пустого строкового значения. Вы можете использовать пару кавычек или String.Empty значение. Последнее иногда предпочтительнее, так как некоторые разработчики считают его более читаемым.

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

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

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

Чтобы включить кавычки необходим специальный код или последовательность Escape-символов. Последовательности Escape-символов предоставляются в виде обратной косой черты ( \ ), за которой следует один или несколько символов, определяющих добавляемую информацию. Для кавычек escape-код является обратной косой чертой и кавычкой (\»). Это показано ниже

Аналогичная проблема существует для символьных литералов, где требуется символ апострофа. Как и следовало ожидать, можно использовать обратную косую черту и апостроф (\’).

Буквальные (дословные) строковые литералы

C# поддерживает второй тип строкового литерала, который известен как дословный (буквальный) строковый литерал. Он не требует использования escape-символов для определения специальных символов. Вместо этого любая информация в исходном коде, включая новые линии, включается в строку. Определение дословного литерала — префикс строки с символом @ перед открывающей кавычкой. Единственный символ, который требует другого действия, — это сама кавычка, которая должна быть введена дважды, чтобы указать один символ.

C++ Programming/Code/IO/Streams/string

Contents

The string class [ edit ]

The string class is a part of the C++ standard library, used for convenient manipulation of sequences of characters, to replace the static, unsafe C method of handling strings. To use the string class in a program, the header must be included. The standard library string class can be accessed through the std namespace.

The basic template class is basic_string<> and its standard specializations are string and wstring .

Basic usage [ edit ]

Declaring a std string is done by using one of these two methods:

Text I/O [ edit ]

This section will deal only with keyboard and text input. There are many other inputs that can be read (mouse movements and button clicks, etc), but these will not be covered in this section, even reading the special keys of the keyboard will be excluded.

Perhaps the most basic use of the string class is for reading text from the user and writing it to the screen. In the header file iostream, C++ defines an object named cin that handles input in much the same way that cout handles output.

The >> operator will cause the execution to stop and will wait for the user to type something. If the user types a valid integer, it will be converted into an integer value and stored in x.

If the user types something other than an integer, the compiler will not report an error. Instead, it leaves the old content (a «random» meaningless value) in x and continues.

This can then be extended into the following program:

Although a string may hold a sequence containing any character—including spaces and nulls—when reading into a string using cin and the extraction operator (>>) only the characters before the first space will be stored. Alternatively, if an entire line of text is desired, the getline function may be used:

Getting user input [ edit ]

Fortunately, there is a way to check and see if an input statement succeeds. We can invoke the good function on cin to check what is called the stream state. good returns a bool: if true, then the last input statement succeeded. If not, we know that some previous operation failed, and also that the next operation will fail.

Thus, getting input from the user might look like this:

cin can also be used to input a string:

As with the scanf() function from the Standard C Library, this statement only takes the first word of input, and leaves the rest for the next input statement. So, if you run this program and type your full name, it will only output your first name.

You may also notice the >> operator doesn’t handle errors as expected (for example, if you accidentally typed your name in a prompt for a number.) Because of these issues, it may be more suitable to read a line of text, and using the line for input — this is performed using the function called getline.

The first argument to getline is cin, which is where the input is coming from. The second argument is the name of the string variable where you want the result to be stored.

getline reads the entire line until the user hits Return or Enter. This is useful for inputting strings that contain spaces.

In fact, getline is generally useful for getting input of any kind. For example, if you wanted the user to type an integer, you could input a string and then check to see if it is a valid integer. If so, you can convert it to an integer value. If not, you can print an error message and ask the user to try again.

To convert a string to an integer you can use the strtol function defined in the header file cstdlib. (Note that the older function atoi is less safe than strtol, as well as being less capable.)

If you still need the features of the >> operator, you will need to create a string stream as available from . The use of this stream will be discussed in a later chapter.

More advanced string manipulation [ edit ]

To do:
Detail the commonly used std::string member functions(partially done)


We will be using this dummy string for some of our examples.

This invokes the default constructor with a const char* argument. Default constructor creates a string which contains nothing, i.e. no characters, not even a ‘\0’ (however std::string is not null terminated).

Will trigger the copy constructor. std::string knows enough to make a deep copy of the characters it stores.

This will copy strings using assignment operator. Effect of this code is same as using copy constructor in example above.

Size [ edit ]

So for example one might do:

The methods size() and length() both return the size of the string object. There is no apparent difference. Remember that the last character in the string is size() — 1 and not size() . Like in C-style strings, and arrays in general, std::string starts counting from 0.

I/O [ edit ]

The shift operators ( >> and ) have been overloaded so you can perform I/O operations on istream and ostream objects, most notably cout , cin , and filestreams. Thus you could just do console I/O like this:

Alternatively, if you want to read entire lines at a time, use getline() . Note that this is not a member function. getline() will retrieve characters from input stream in and assign them to str until EOF is reached or delim is encountered. getline will reset the input string before appending data to it. delim can be set to any char value and acts as a general delimiter. Here is some example usage:

Because of the way getline works (i.e. it returns the input stream), you can nest multiple getline() calls to get multiple strings; however this may significantly reduce readability.

Илон Маск рекомендует:  Что такое код pdf_setflat

Operators [ edit ]

Chars in string s can be accessed directly using the overloaded subscript ( [] ) operator, like in char arrays:

std::string supports casting from the older C string type const char* . You can also assign or append a simple char to a string. Assigning a char* to a string is as simple as

If you want to do it character by character, you can also use

Not surprisingly, operator+ and operator+= are also defined! You can append another string , a const char* or a char to any string.

The comparison operators >, =, all perform comparison operations on strings, similar to the C strcmp() function. These return a true/false value.

Searching strings [ edit ]

You can use the find() member function to find the first occurrence of a string inside another. find() will look for needle inside this starting from position pos and return the position of the first occurrence of the needle . For example:

Will simply print «4» which is the index of the first occurrence of «o» in str . If we want the «o» in «World», we need to modify pos to point past the first occurrence. str.find(find, 4) would return 4, while str.find(find, 5) would give 7. If the substring isn’t found, find() returns std::string::npos .This simple code searches a string for all occurrences of «wiki» and prints their positions:

The function rfind() works similarly, except it returns the last occurrence of the passed string.

Inserting/erasing [ edit ]

You can use the insert() member function to insert another string into a string. For example:

Would return Hello Human World!

You can use erase() to remove a substring from a string. For example:

Would return Hello!

You can use substr() to extract a substring from a string. For example:

Would return World.

Backwards compatibility [ edit ]

For backwards compatibility with C/C++ functions which only accept char* parameters, you can use the member functions string::c_str() and string::data() to return a temporary const char* string you can pass to a function. The difference between these two functions is that c_str() returns a null-terminated string while data() does not necessarily return a null-terminated string. So, if your legacy function requires a null-terminated string, use c_str() , otherwise use data() (and presumably pass the length of the string in as well).

String Concatenation [ edit ]

Strings can be concatenated(appended) together by simply using the + operator

Output here will be «Hello World»

Appending Strings [ edit ]

Another thing to note is that instead of the + operator or concatenation, the .append(str2) class member function can be used to concatenate one string to another. The str2 object is permitted to be a string object or a C-string. This will add the string in the parenthesis to the string which is calling append .

It should also be noted that the append function can be used to append a string at a specific character location in the string. If a programmer puts str.append(str2, p, n) , n number of characters from position p in string str2 will be appended to the end of str . For example, in the following code, there are two strings. 5 characters from the second string, starting with position 8 of str2 will be appended to the end of the first string, str .

The code above will append the word Bears to the end of the first string, and then print Watch out for Bears on the screen.

String Conversion to signed integer [ edit ]

Sometimes we want to convert strings into numbers. To do so we can use stoi() function which takes a string as an argument and returns the value.

To convert these strings to a number we save the stoi of the string variable in an integer variable.

Output will be: Before stoi string :12023 and after stoi int: 12023 Before stoi string :1 and after stoi int: 1 Before stoi string :1232 and after stoi int: 1232

Integer Conversion to String [ edit ]

If we instead want to do the opposite and convert an integer to a string, we can use the to_string() function, which takes an integer as an argument and returns the integer as a string.

To convert this integer to a string, we call the to_string() function. The integer variable is passed into the function as an argument. The function will then return that integer as a string, which can then be assigned to a string variable.

Output will be: Before to_string int:12023 and after to_string string:12023

String Formatting [ edit ]

Strings can only be appended to other strings, but not to numbers or other datatypes, so something like std::string(«Foo») + 5 would not result in a string with the content «Foo5» . To convert other datatypes into string there exist the class std::ostringstream , found in the include file . std::ostringstream acts exactly like std::cout , the only difference is that the output doesn’t go to the current standard output as provided by the operating system, but into an internal buffer, that buffer can be converted into a std::string via the std::ostringstream::str() method.

Example [ edit ]

Advanced use [ edit ]

To do:
Template parameters to basic_string etc.

Классы string и wstring. Часть 5

Класс string стандартной библиотеки C++ хорошо известен и охотно используем. Но не все и не всегда задумываются над тем, что класс string , при некоторых отличиях в деталях — это и есть контейнер вектор: vector char > . Правда, он дополнен некоторыми особенностями (но такой код и вы сами могли бы написать):

Метод size() задублирован методом length(). Они полностью тождественны, из соображений удобства. Просто для строки естественнее иметь длину, чем размер;

Определены перегруженные операции + , += которые возвращают конкатенацию (объединение) строк;

Определён конструктор, инициализирующий string при создании начальным значением символьной строки в формате ASCIIZ ( char* — указатель на символьный массив в стиле C завершающийся нулём);

Определён метод c_str() , который возвращает указатель на внутреннее содержимое строки в формате ASCIIZ. Поскольку это внутреннее значение, его можно использовать, но не стоит пытаться его изменять. Это хорошо не закончится.

Во всём же остальном строки ведут себя точно как вектор, и к ним применимы все операции над векторами. Понимание того, что представляет собой класс string ( vector ) может позволить создать ряд неожиданных эффектов. Например, поскольку нулевой символ не имеет для vector никакого особого значения (в отличие от строки C), то его тоже вполне можно «заталкивать» в конец string. Тем самым можно поместить в единственную переменную string целый массив C-строк или даже целый текст.

Вот, как подобным образом поместить весь набор переменных окружения ( environment ) операционной системы в одну переменную string :

Примечание: здесь мы использовали ещё одну из допускаемых форм главной функции программы main() – 3-м параметром которой является массив указателей строк ( char* ) переменных окружения ( environment ). Признаком окончания массива строк envp является указатель NULL (тех. документация операционной системы).

Что относительно типа wstring ? wstring — это эквивалент vector wchar_t > , вектор «широких», локализованных символов, представляющих интернациональную кодировку символами Unicode. Разбор содержимого (поиск, выделение слов, разбиение на строки и т.д.) русскоязычной или любой другой строки (китайской например) можно делать только в формате wtring (не string ).

При этом необходимо предварительно установить правильную локаль для программы (локаль по умолчанию “С” предполагает только ASCII символы в 7-битном представлении). А для ввода-вывода wstring предлагаются потоки, соответственно wcin и wcout , вместо cin и cout , предназначенных для string . Это написано для напоминания.

В порядке иллюстрации рассмотрим анализ локализованной строки wstring на предмет того, является ли она палиндромом.

Пробелы и знаки препинания при сравнениях пропускаются:

§13 Символьный тип. C-строка. Объекты класса string. Интернационализация

Символьный тип. Строки в стиле Cи

Символьный тип char

В C++ символьный тип данных char относится к целым типам, так как в переменных этого типа скрыт код символа в кодовой таблице (ASCII). Размер памяти, отводимой на хранение объекта типа char , равен 1 байт.
Также как и другие целые типы, тип char имеет две формы знаковую ( signed ) и беззнаковую ( unsigned ). Если используется беззнаковая форма char , то переменной этого типа можно присваивать целочисленные значения в промежутке [0; 255]. Таким образом, символьное значение будет передаваться по коду. Если же переменная инициализируется или ей присваивается символьное значение, то символьный литерал (один или два символа) должен заключаться в апострофы (одинарные кавычки):

Заметим, однако, что если локаль не определена, то корректно обрабатываться будут только символы первой части таблицы ASCII (“аски”), поскольку она входит в таблицу кодировки UTF-8 без изменений, включая размер отводимый для хранения символа, т. е. 1 байт.

Во время компоновки программы (ниже) компилятор выдаст предупреждение, что ‘Б’ : “ многознаковая символьная константа [-Wmultichar] ” и “ переполнение при неявном преобразовании константы [-Woverflow] “.

Это означает, что символ ‘Б’ не является однобайтовым. Запустить программу, конечно можно, но символ ‘Б’ не будет обработан корректно. Чтобы решить данную проблему нужно включить локализацию и использовать для работы с символами специальный тип широких символов wchar_t . Как это сделать мы обсудим ниже.
Поскольку, как мы уже сказали, тип char является целочисленным, то нередко можно встретить такой трюк для преобразования символ => число:

Объясняется это просто. Код символа ‘0’ равен 48, а код ‘7’ равен 55. Разница кодов дает цифру 7, которая неявно преобразуется к типу int .
Вывод символов по их кодам можно осуществить путем явного преобразования типа следующим образом:

Получить символы можно и другими способами. Так в классе istream определен соответствующий метод get() :

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

Си-строка

C-строка представляет собой массив символов последним элементом которого является нулевой символ — ‘\0’ (NULL) (не путать с символом ‘0’ ). Символьный C-массив должен объявляться следующим образом:

В символьный массив S2 войдет ровно 19 символов и завершающий символ (т. е. всего 20). В массив S1 завершающий символ будет добавлен автоматически. В С++ С-строка имеет тип const char* . Недостатки присущие C-массивам в равной степени относятся и к С-строкам, как к символьным массивам. Здесь более подробно освещается работа с С-строкой. В продолжении нашего курса для работы со строкой будет использоваться исключительно класс string о котором и пойдет речь в следующих разделах.

Класс string. Создание объектов класса string

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

В отличие от статического символьного C-массива, контейнер string представляет строку как динамический массив. Для работы с контейнером string стандартная библиотека предоставляет полный набор функций, общий для всех контейнеров, в частности, функции получения итераторов. Вся работа по распределению памяти выполняется классом string , а не разработчиком. Для начала работы с классом string необходимо подключить заголовочный файл директивой:

Инициализация объекта класса string

Объявление переменных типа string (на языке ООП — создание экземпляра класса) не отличается от объявления переменных других типов:

Инициализировать объекты класса string можно различными способами.

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

Ввод и вывод объектов класса string. Функция getline

Для ввода/вывода строки используются глобальные потоковые объекты cin и cout и операции >> и , соответственно. Если установлен флаг skipws начальные пробелы будут проигнорированы. В поток будут поступать символы, пока не будет встречен пробельный символ (к ним относятся следующие: собственно пробел, \t , \v , \r , \n и \f ) или EOF . В программе пробельный символ будет расцениваться как нулевое значение (т. е. конец строки) и в строковую переменную будет записана только часть строки, до первого пробела, остальная часть останется на очереди — в потоке. Такую строку получает переменная myStr в программе ext_13.4. Произвести сцепку оставшихся слов в потоке можно циклом while. (Смотрите ниже).

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

Синтаксис этой функции таков:


Аргументы функции:
is — потоковый объект ввода;
str — объект класса string , куда будет помещена строка;
char_delim — третий аргумент (необязательный) используется для указания завершающего символа. Если третьего аргумента нет, то предполагается, что завершающий символ — ‘\n’ .
Функция считывает строку полностью с завершающим символом. Сам символ новой строки отбрасывается и в строковой переменной не сохраняется. Также не добавляется нулевой символ. Символ новой строки завершает выполнение функции, где бы он не находился.
Если нулевой символ находится в начале строки, то функция вернет пустую строку. Рассмотрим этот случай:

После выполнения инструкции в строке 14: getline(cin, S2); и вывода C-строки программа выводит пустую строку и завершает свою работу. Причина того, что getline не приняла новую строку в том, что поток ввода оказался не пуст! После ввода С-строки ( ch ) в потоке остается завершающий символ (после выполнения операции ввода), этот символ и перехватила функция getline . Для очистки потока можно применить функцию cin.ignore() (или cin.get() ).

Локализация. Строки широких символов

Для работы с кодировками символов, которые превышают 8 бит (более «широких») предусмотрен специальный символьный тип wchar_t (wide character). Размер этого типа определяется компилятором и платформой. Так в Windows API wchar_t имеет фиксированный размер 16 бит (что не позволяет закодировать весь набор символов Unicode), а в GNU/Linux — 32 бита.
Прежде чем использовать строку широких символов, необходимо установить «локальный контекст» определяющий в какой кодировке широкий символ будет передаваться в потоки и какой, при этом, национальный стандарт будет использован для форматирования. Для этого необходимо включить директиву:

В самой программе необходимо создать глобальный объект локального контекста, который соответствует локали окружения. Функции-члену locale() передается строка — имя локального контекста (для Linux это ru_RU.UTF-8 ), как аргумент:

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

В одной программе можно использовать несколько локальных контекстов.
Для работы с широкими символами существуют специальные версии типов и классов, которые используются с префиксом «w» : wstring , wcout , wcin и т. п. Строка-литерал широких символов должна иметь суффикс «L» .
Постановка задачи. Дана строка G1, содержащая как русские, так и английские буквы и символ G2. Подсчитать общее количество символов, равных G2 в строке G1. Если таких символов нет, то сообщить об этом. Получить новую строку символы которой записаны в обратном порядке.

Объект класса string как динамический массив

Объект класса string является динамическим массивом. Это означает, что для хранения добавляемых элементов необходимо увеличивать (или уменьшать) размер памяти, отводимой под хранение модифицированной строки. Иначе, эти процессы называются выделением памяти и освобождением памяти. Эту работу берет на себя класс string . Для контейнеров, которые содержат динамически изменяемые структуры данных, существуют два понятия размер и объем. Смысл этих понятий следующий. При создании объекта класса string в памяти резервируется некоторое адресное пространство, даже если начальный размер массива нулевой, т. е. строка не инициализирована. Размер массива определяется количеством символов, хранящихся в массиве. Размер можно получить с помощью метода size() . При добавлении элементов, объем массива (т. е. выделенная память для хранения элементов) может увеличится автоматически. Это приведет к процессу перераспределения памяти во время которого все элементы массива будут перенесены в новую область памяти. Для предотвращения этого процесса, настоятельно рекомендуется резервировать емкость строки методом reserve() . (Это, не означает запрет на увеличение или уменьшение массива. При необходимости, память может быть увеличена автоматически, если количество элементов станет больше). Для того, чтобы узнать какое количество символов может принять контейнер без увеличения объема, используется метод capacity() . Существует необязательный (для выполнения классом string ) метод shrink_to_fit() , который запрашивает уменьшение объема capacity до size .
Контейнер string устроен так, что вставка и удаление символа в конце массива производится очень быстро. Однако, операции вставки в произвольную позицию являются мало эффективными, с точки зрения скорости работы программы. Дело в том, что классу приходится выполнять работу по сдвигу массива, а заодно, если потребуется, то и работу по выделению памяти, чтобы принять новые элементы. А это, зачастую, приводит к процессу перераспределения памяти.
Для вставки и удаления символов в конце массива используются методы push_back() и pop_back()

Илон Маск рекомендует:  Как сделать многострочную надпись на tbitbtn
push_back

Этот метод добавляет переданный символ к концу строки. Это позволяет произвести «сборку строки» по определенным критериям. Рассмотрим задачу.

Дано четное число N (> 0) и символы C1 и C2 . Вывести строку длины N , которая состоит из чередующихся символов C1 и C2 , начиная с C1 .

pop_back

Этот метод, напротив, удаляет элемент в конце строки.

Конкатенация. Метод append()

В операции конкатенации (лат. concatenatio — «сцепле́ние») происходит объединение строк. Результатом сложения двух строк — является новая строка string. В этих операциях могут участвовать наряду с объектами класса string строковые литералы, одиночные символы и C-строки. Если объекту класса string осуществляется попытка присвоить значение конкатенации литералов (в конкатенации не участвуют объекты класса string ), то это приведет к ошибке. Для конкатенации применяются операции «+» и «+=» . Перегруженная операция «+=» трактуется как «добавление в конец» строки. Например:

В результате выполнения первой инструкции в str3 будет записана вначале строка str1 , а затем строка str2 . (Значение, которое строка str3 имела до операции присваивания, будет утрачено). Результат второй инструкции: к символам строки str1 будут дописаны символы строки str3 . В третьей инструкции строке str2 будет передано значение строки str1 , дополненной литералом.

append()

Фактически операция «+=» может, в некоторых случаях, заменить существующий метод append() . Однако, данный метод гибче, так как позволяет добавлять элементы различными способами, например:
1. подстроку из строки other_str в промежутке [pos, pos + count) :

2. подстроку определённую итераторами first и last :

3. или более лаконичное добавление count символов ch :

Дана непустая строка S и целое число N (> 0) . Вывести строку, содержащую символы строки S , между которыми вставлено по N символов ‘*’ .

Лексикографическое сравнение

Лексикографический порядок можно представить в виде следующей последовательности: А . Вначале сравниваются первые элементы, если они равны, то сравниваются вторые, затем третьи и т. д. Для сравнения строк используются обычные операции сравнения == , != , , , > , >= . В качестве операндов в операциях сравнения могут выступать объекты типа string, C-строки и строковые литералы. Например:

Что делает этот string.format код кода?

У меня есть эта часть кода в С#:

Я пытаюсь понять, какая часть начинается с «getColumnName?» и заканчивается символом «.ToString()». Я понял, что это тип system.object, но я понятия не имею, что конкретно он делает или как он работает. Я хочу, чтобы из-за этого: «читатель» имел несколько строк в нем, и я хочу писать только определенные строки.

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

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

Если getColumnName возвращает значение true, оно выводит имя столбца между тегами

, в противном случае — значение данных.

Чтобы дополнительно деконструировать:

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

Это называется условным оператором.

Аргумент getColumnName оценивается, и если true, возвращается первый аргумент после ? , если false, второй.

Итак, если getColumnName == true, вы увидите

NAME

else

Value

Это похоже на следующий

Потому что getColumnName имеет тип bool, поэтому нет необходимости проверять его как

Вы можете написать это как

String.Format(строка, метод)

И метод String.Format заменяет элементы в указанной строке заданным объектом, этот метод имеет два аргумента, первый из которых является строкой, а второй — объектом. например

Вопрос № 11 ответил Адам Вчера

Как вы можете видеть, <0>заменяется на 11, а <1>заменяется на Адама, а <2>заменяется на Вчера.

is_serialized_string() WP 2.0.5

Проверяет находится ли «строка» в переданных сериализованных данных. Т.е. была ли серриализованна отдельная строка.

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

Возвращает

True/false. False — это не сериализованная строка. true — это сериализованные данные и в них находится строка.

Использование

Примеры

#1 Демонстрация проверки сериализованной строки.

#2 Эту функцию можно принято использовать в связке с is_serialized()

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

Приведение и проверка типов

Операторы is и !is

Мы можем проверить принадлежит ли объект к какому-либо типу во время исполнения с помощью оператора is или его отрицания !is :

Умные приведения

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

Компилятор достаточно умён для того, чтобы делать автоматические приведения в случаях, когда проверка на несоответствие типу ( !is ) приводит к выходу из функции:

или в случаях, когда приводимая переменная находится справа от оператора && или || :

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

  • с локальными val переменными — всегда;
  • с val свойствами — если поле имеет модификатор доступа private или internal , или проверка происходит в том же модуле, в котором объявлено это свойство. Умные приведения неприменимы к публичным свойствам или свойствам, которые имеют переопределённые getter’ы;
  • с локальными var переменными — если переменная не изменяется между проверкой и использованием и не захватывается лямбдой, которая её модифицирует;
  • с var свойствами — никогда (потому что переменная может быть изменена в любое время другим кодом).

Оператор «небезопасного» приведения

Обычно оператор приведения выбрасывает исключение, если приведение невозможно, поэтому мы называем его небезопасным. Небезопасное приведение в Kotlin выполняется с помощью инфиксного оператора as (см. приоритеты операторов):

Заметьте, что null не может быть приведен к String , так как String не является nullable, т.е. если y — null, код выше выбросит исключение. Чтобы соответствовать семантике приведений в Java, нам нужно указать nullable тип в правой части приведения:

Оператор «безопасного» (nullable) приведения

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

Заметьте, что несмотря на то, что справа от as? стоит non-null тип String , результат приведения является nullable.

Стирание и проверка типов у Обобщений (Generics)

Котлин обеспечивает типобезопасность операций, связанных с обобщениями на этапе компиляции(compile time), в то время как информация о типе аргумента обобщения недоступна во время выполнения программы. Например для List происходит стирание типа, что превращает его в List . В связи с чем, нет способа проверить, принадлежит ли объект конкретному типу во время выполнения программы.

` or `list is T` (type parameter). You can, however, check an instance against a [star-projected type](generics.html#star-projections):

Учитывая это, компилятор запрещает is-проверки, которые не могут быть выполнены во время выполнения программы из-за стирания типов, например ints is List или list is T (параметризированный тип). Однако у вас есть возможность произвести проверку со «Звёздными» проекциями:

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

Аналогичный синтаксис, с пропущенным типом аргумента может использоваться для приведений, которые не принимают типы аргументы: list as ArrayList

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

Непроверяемые (Unchecked) приведения

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

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

Компилятор выдает предупреждение для приведения в последней строке. Приведение не может быть полностью проверено во время выполнения и нет дает гарантии, что значения в словаре (map) являются Int .

` and `DictionaryWriter ` with type-safe implementations for different types. You can introduce reasonable abstractions to move unchecked casts from calling code to the implementation details. Proper use of [generic variance](generics.html#variance) can also help.

Чтобы избежать непроверяемые приведения, вы можете изменить структуру программы: в примере выше, возможно объявить интерфейсы DictionaryReader и DictionaryWriter с типобезопасными имплементациями для типизированного типа. Правильно использование вариативности обобщений также может помочь.

Для обобщенных функций, используемых встроенные (inline) функции с параметрами вещественного типа приведение типа arg as T является проверяемым, до тех пор, если тип arg не имеет свои аргументы типа, которые были стерты.

Предупреждение о непроверяемом приведении можно убрать используя аннотации

`) retain the information about the erased type of their elements, and the type casts to an array type are partially checked: the nullability and actual type arguments of the elements type are still erased. For example, the cast `foo as Array
?>` will succeed if `foo` is an array holding any `List `, nullable or not.

В JVM, массивы сохраняют информацию о стираемом типе их элементов, а также приведение типов к массиву частично проверяется: nullability и фактические аргументы для параметризированных элементов массива все еще стираются. Например, приведение foo as Array
?> будет успешным, если foo является массивом, содержащим какой-либо List , null или нет.


Что такое docstring? С чем его едят?

Taras

Docstring — это такая строковая переменная, которая идет сразу за объявлением модуля, функции, класса, метода. Таким образом питон предоставляет удобный способ добавления документации. Существует много средств для автоматического генерирования документации, которые используют докстринг. Докстринг очень похож на комментарий, но заклчается в тройные кавычки. Все функции должны иметь докстринг, который содержит описание работы этой функции. Комментарии же обычно пытаются объяснить эту работу. На первой строке пишем короткую фразу, такую как: «Решает уравнение ax^2+ bx + c =0″. Ниже идет более подробный разбор различный случаев. Достать соответсвующий докстринг можно, обратившись к атрибуту doc объекта.

Примеры использования

Теперь давайте посмотрим на докстринг в действии.

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

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

Для чего ещё может быть использован докстринг, описано в документе PEP 257.

Получи опыт серьезной веб-разработки

Python, Git, API ВКонтакте и Facebook, Django, Linux и не только. Выбери с какого модуля начать.

C++ — Преобразования Unicode-кодировок с помощью STL-строк и Win32 API

Продукты и технологии:

C++ Unicode, Win32 API, STL-строки

В статье рассматриваются:

  • кодировки UTF-8 и UTF-16;
  • преобразование между UTF-8 и UTF-16 через Win32 API;
  • обертывание MultiByteToWideChar в C++-функцию в современном стиле для преобразований UTF-8 в UTF-16;
  • обработка ошибок с помощью C++-исключений.

Unicode является стандартом де-факто для представления текста на любых языках в современном программном обеспечении. Согласно веб-сайту официального консорциума Unicode (bit.ly/1Rtdulx), “Unicode предоставляет уникальное число для каждого символа независимо от платформы, программы и языка.” Каждое из этих уникальных чисел называют кодовой точкой (code point), и обычно она представляется с использованием префикса “U+”, за которым следует уникальное число в шестнадцатеричной форме. Так, кодовая точка, связанная с символом “C” — U+0043. Заметьте, что Unicode является отраслевым стандартом, охватывающим большинство систем письма в мире, включая иероглифы. Например, японская кана (ru.wikipedia.org/wiki/%D0%9A%D0%B0%D0%BD%D0%B0) содержит иероглиф 学 (среди других значений обозначающий обучение и знание), который связан с кодовой точкой U+5B66. В настоящее время в стандарте Unicode определены более 1 114 000 кодовых точек.

От абстрактных кодовых точек к реальным битам: кодировки UTF-8 и UTF-16

Кодовая точка — это абстрактная концепция. Для программиста вопрос заключается в том, как эти кодовые точки Unicode представляются в конкретном виде, используя биты? Ответ на этот вопрос ведет прямо к концепции Unicode-кодировки. По сути, Unicode-кодировка — конкретный, четко определенный способ представления значений кодовых точек Unicode в битах. В стандарте Unicode определено несколько кодировок, но наиболее важные из них — UTF-8 и UTF-16; обе они являются кодировками переменной длины (variable-length encodings), способными кодировать все возможные символы Unicode или, точнее, кодовые точки. Поэтому преобразования между этими двумя кодировками осуществляются без потерь: ни один символ Unicode не будет потерян в ходе этого процесса.

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

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

Если вспомнить о ранее упомянутых двух Unicode-символах, то заглавная буква “C” (кодовая точка U+0043) кодируется в UTF-8 одним байтом 0x43 (значение 43 шестнадцатеричное), который точно соответствует ASCII-коду, связанному с этим символом (поскольку UTF-8 обратно совместима с ASCII). Напротив, японский иероглиф 学 (кодовая точка U+5B66) кодируется в UTF-8 как последовательность из трех байтов: 0xE5 0xAD 0xA6.

UTF-8 — самая популярная Unicode-кодировка в Интернете. Согласно последним статистическим сведениям W3Techs, доступным по ссылке bit.ly/1UT5EBC, UTF-8 используется на 87% всех проанализированных веб-сайтов.

UTF-16 фактически является стандартом де-факто кодировки, используемой Windows API-функциями с поддержкой Unicode. UTF-16 — “родная” Unicode-кодировка и во многих других программных системах. Так, Qt, Java, библиотека International Components for Unicode (ICU) и прочие используют кодировку UTF-16 для хранения Unicode-строк.

В UTF-16 применяются 16-битные кодовые единицы. Как и UTF-8, UTF-16 позволяет кодировать все возможные в Unicode кодовые точки. Однако, если UTF-8 кодирует каждую допустимую в Unicode кодовую точку, используя от одного до четырех восьмибитовых байтов, UTF-16 в каком-то смысле проще. По сути, кодовые точки Unicode кодируются в UTF-16 всего одним или двумя 16-битными кодовыми единицами. Но наличие кодовых единиц размером больше одного байта влечет за собой проблемы с порядком следования байтов: фактически существует и “остроконечная” (big-endian) (от младшего байта к старшему) UTF-16, и “тупоконечная” (little-endian) (от старшего байта к младшему) UTF-16. Тогда как кодировка UTF-8 только одна, поскольку нейтральна к порядку следования байтов.

В Unicode определена концепция плоскости (plane) как непрерывной группы 65 536 (2 16 ) кодовых точек. Первая плоскость идентифицируется как плоскость 0, или Basic Multilingual Plane (BMP). Символы почти для всех современных языков и многие знаки находятся в BMP, а все эти BMP-символы представляются в UTF-16 одной 16-битной кодовой единицей.

Дополнительные символы (supplementary characters) расположены в плоскостях, отличных от BMP; они включают пиктографические символы вроде Emoji и символы вышедших из употребления письменностей наподобие египетских иероглифов. Эти дополнительные символы вне BMP кодируются в UTF-16 двумя 16-битными кодовыми единицами, также известными как суррогатные пары (surrogate pairs).

Заглавная буква “C” (U+0043) кодируется в UTF-16 как одна 16-битная кодовая единица 0x0043, а иероглиф 学 (U+5B66) — как одна 16-битная кодовая единица 0x5B66. Для многих Unicode-символов существует прямое соответствие между их абстрактным представлением в виде кодовой точки (скажем, U+5B66) и их шестнадцатеричной кодировкой в UTF-16 (например, 16-битное слово 0x5B66).

Чтобы слегка поразвлечься, давайте посмотрим на некоторые пиктографические символы. Unicode-символ “снеговик” ( , U+2603) кодируется в UTF-8 как трехбайтовая последовательность: 0xE2 0x98 0x83; однако его эквивалент в UTF-16 является одной 16-битной кодовой единицей 0x2603. Unicode-символ “кружка пива” ( , U+1F37A), находящийся вне BMP, кодируется в UTF-8 как четырехбайтовая последовательность: 0xF0 0x9F 0x8D 0xBA. Его эквивалент в UTF-16 использует две 16-битные кодовые единицы, 0xD83C 0xDF7A, которые являются примером суррогатной пары в UTF-16.

Преобразования между UTF-8 и UTF-16 через Win32 API

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

Сравнительно недавно стало общепринятым хранить Unicode-текст, закодированный в UTF-8, в экземплярах класса std::string в кросс-платформенном C++-коде. Более того, существует принципиальное соглашение о том, что UTF-8 является лучшей кодировкой для обмена текстом через границы приложений и разные аппаратные платформы. Тот факт, что UTF-8 нейтральна к порядку следования байтов, сыграл важную роль в этом. В любом случае преобразования между UTF-8 и UTF-16 необходимы по крайней мере на границе Win32 API, поскольку Windows-функции с поддержкой Unicode использую UTF-16 в качестве “родной” кодировки.

Илон Маск рекомендует:  Использование оператора exists

Теперь углубимся в код на C++, который реализует эти преобразования между Unicode-кодировками UTF-8 и UTF-16. Для этой цели можно использовать две ключевые Win32-функции: MultiByteToWideChar и симметричную ей WideCharToMultiByte. Первую вызывают для преобразования из UTF-8 (“многобайтовой” строки в специфической терминологии Win32 API) в UTF-16 (“широкобайтовую” строку), а вторую — для обратной задачи. Поскольку эти Win32-функции имеют сходные интерфейсы и шаблоны использования, в этой статье я сосредоточусь только на MultiByteToWideChar, но в сопутствующий компилируемый C++-код включена и другая функция.

Применение стандартных строковых STL-классов для хранения Unicode-текста Поскольку это статья по C++, справедливо ожидать хранения Unicode-текста в одном из строковых классов. Теперь возникает вопрос: какой вид строковых C++-классов можно использовать для хранения Unicode-текста? Ответ зависит от конкретной кодировки Unicode-текста. Если применяется кодировка UTF-8, то, поскольку она основана на восьмибитовых кодовых единицах, для представления каждой из этих единиц в C++ годится простой char. В этом случае STL-класс std::string, основанный на char, — хороший вариант для хранения Unicode-текста в кодировке UTF-8.

С другой стороны, если Unicode-текст закодирован в UTF-16, каждая кодовая единица представляется 16-битными словами. В Visual C++ тип wchar_t имеет как раз размер в 16 бит; соответственно STL-класс std::wstring, основанный на wchar_t, отлично подходит для хранения Unicode-текста в кодировке UTF-16.

Стоит отметить, что стандарт C++ не определяет размер типа wchar_t, поэтому, хотя компилятор Visual C++ использует для него 16 битов, другие компиляторы C++ могут оперировать другими размерами. И фактически размер wchar_t, определяемый компилятором GNU GCC C++ в Linux, составляет 32 бита. Так как тип wchar_t имеет разные размеры в разных компиляторах и платформах, класс std::wstring, основанный на этом типе, не является портируемым. Иначе говоря, wstring можно использовать для хранения Unicode-текста, закодированного в UTF-16, в Windows с компилятором Visual C++ (где размер wchar_t равен 16 битам), но не в Linux с компилятором GCC C++, который определяет 32-битный тип wchar_t.

На самом деле существует еще одна Unicode-кодировка, менее известная и реже используемая на практике, чем ее родственники: UTF-32. Как и предполагает ее название, она основана на 32-битных кодовых единицах. Поэтому 32-битный wchar_t из GCC/Linux — хороший кандидат для кодировки UTF-32 на платформе Linux.

Этой неоднозначностью размера wchar_t определяется отсутствие портируемости C++-кода, оперирующего с этим типом (включая сам класс std::wstring). С другой стороны, std::string, основанный на char, является портируемым. Но с практической точки зрения, следует отметить, что применение wstring для хранения текста в кодировке UTF-16 прекрасно подходит в C++-коде, специфичном для Windows. По сути, эти части кода уже взаимодействуют с Win32 API, которые специфичны для платформы просто по определению. Поэтому добавление wstring в смесь никак не меняет ситуацию.

Наконец, важно отметить, что из-за варьируемой длины в кодировках UTF-8 и UTF-16 возвращаемые значения методов string::length и wstring::length, как правило, не соответствуют количеству Unicode-символов (или кодовых точек), хранимых в строках.

Интерфейс функции преобразования Давайте разработаем функцию для преобразования Unicode-текст, закодированного в UTF-8, в эквивалентный текст с кодировкой UTF-16. Она может оказаться удобной, например когда у вас есть какой-то кросс-платформенный C++-код, который хранит Unicode-строки в кодировке UTF-8, используя STL-класс std::string, и вы хотите передавать этот текст в Win32-функции с поддержкой Unicode, обычно использующие кодировку UTF-16. Поскольку этот код взаимодействует с Win32 API, он уже не является портируемым, так что в данном случае прекрасно подходит std::wstring. Возможный прототип функции выглядит так:

Эта функция преобразования принимает в качестве ввода Unicode-строку в кодировке UTF-8, которая хранится в стандартном STL-классе std::string. Поскольку это входной параметр, он передается в функцию по const-ссылке (const &). После преобразования возвращается строка, закодированная в UTF-16, хранящаяся в экземпляре std::wstring. Однако при конвертации Unicode-кодировок вполне возможны проблемы. Например, входная строка UTF-8 может содержать недопустимую для UTF-8 последовательность (что может быть результатом ошибки в других частях кода или неких злонамеренных действий). В таких случаях самое лучшее с точки зрения безопасности — прекращать преобразование как неудавшееся вместо использования потенциально опасной последовательности байтов. Функция преобразования может обрабатывать случаи с недопустимыми входными последовательностями UTF-8, генерируя C++-исключение.

Определение класса исключения для ошибок преобразования Какого рода C++-класс можно применить для генерации исключения в случае неудачного преобразования Unicode-кодировки? Как вариант можно было бы использовать класс, уже определенный в стандартной библиотеке, например std::runtime_error. Однако я предпочитаю определить новый пользовательский C++-класс исключения для этой цели, унаследовав его от std::runtime_error. Когда Win32-функции вроде MultiByteToWideChar выдают ошибку, есть возможность вызывать GetLastError, чтобы получить подробную информацию о причине неудачи. Так, в случае недопустимых последовательностей UTF-8 во входной строке типичный код ошибки, возвращаемый GetLastErorr, — ERROR_NO_UNICODE_TRANSLATION. Имеет смысл добавить эту часть информации в пользовательский C++-класс исключения; она может оказаться полезной при отладке. Определение этого класса исключения начинается с:

Заметьте, что значение, возвращаемое GetLastError, имеет тип DWORD, который представляет 32-битное целое без знака. Однако DWORD является не портируемым typedef, специфичным для Win32. Но, даже если этот C++-класс исключения генерируется из Win32-специфичных частей C++-кода, он может быть захвачен кросс-платформенным C++-кодом! Поэтому имеет смысл использовать портируемые typedef вместо специфичных для Win32; uint32_t — пример такого типа.

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

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

Поскольку этот класс наследует от std::runtime_error, можно вызвать метод what, чтобы получить сообщение об ошибке, переданное в конструктор. Заметьте, что в определении этого класса используются лишь портируемые стандартные элементы, так что этот класс отлично работает в кросс-платформенных частях C++-кода, даже находящихся далеко от специфичной для Windows точки генерации.

Преобразование из UTF-8 в UTF-16: MultiByteToWideChar в действии

Теперь, когда определен прототип функции преобразования и реализован пользовательский C++-класс исключения для корректного представления ошибок преобразования UTF-8, пора создавать тело этой функции. Как и ожидалось, преобразование из UTF-8 в UTF-16 можно выполнить с помощью Win32-функции MultiByteToWideChar. Термины “многобайтовый” и “широкосимвольный” имеют исторические корни. Изначально эта API-функция и симметричная ей WideCharToMultiByte предназначались в основном для преобразования в Win32-функциях с поддержкой Unicode между текстом, хранящемся в специфических кодовых страницах, и Unicode-текстом, который использует кодировку UTF-16. Широкий символ относится к wchar_t, поэтому он связан со строкой на основе wchar_t, которая является строкой в кодировке UTF-16. Многобайтовая строка, напротив, — это последовательность байтов, выраженная в кодовой странице. Устаревшая концепция кодовой страницы была потом расширена для включения кодировки UTF-8.

Типичный шаблон использования этой API-функции заключается в том, что сначала вы вызываете MultiByteToWideChar, чтобы получить размер конечной строки. Затем создаете некий строковый буфер с этим размером. Обычно это делается вызовом метода std::wstring::resize в случае, если в нем будет храниться строка в кодировке UTF-16. (Подробнее об этом см. мою статью “Using STL Strings at Win32 API Boundaries” за июль 2015 года по ссылке msdn.com/magazine/mt238407.) Наконец, функция MultiByteToWideChar вызывается во второй раз для выполнения преобразования кодировки, используя ранее созданный строковый буфер. Заметьте, что тот же шаблон применяется и к симметричной API-функции WideCharToMultiByte.

Давайте реализуем этот шаблон в коде на C++, в теле пользовательской функции Utf8ToUtf16. Начнем с обработки особого случая с пустой входной строкой, где возвращается пустая выходная строка типа wstring:

Флаги преобразования MultiByteToWideChar можно вызвать в первый раз для получения размера конечной строки в UTF-16. Эта Win32-функция имеет сравнительно сложный интерфейс, и ее поведение определяется согласно флагам. Поскольку эта API-функция будет вызываться в теле функции преобразования Utf8ToUtf16 дважды, хорошая практика для надежности кода и удобства его сопровождения — определять именованную константу, которая может использоваться в обоих вызовах:

Безусловный провал преобразования, если во входной строке обнаруживается недопустимая для UTF-8 последовательность, является хорошей практикой и с точки зрения безопасности. Применение флага MB_ERR_INVALID_CHARS также приветствуется в книге Майкла Говарда (Michael Howard) и Дэвида Лебланка (David LeBlanc) “Writing Secure Code, Second Edition” (Microsoft Press, 2003).

Если ваш проект использует более старую версию компилятора Visual C++, которая не поддерживает ключевое слово constexpr, вы можете заменить его на static const в этом контексте.

Длины строк и безопасные преобразования из size_t в int MultiByteToWideChar ожидает, что параметр с длиной входной строки выражен типом int, тогда как метод length в строковых STL-классах возвращает значение типа, эквивалентного size_t. В 64-разрядных сборках компилятор Visual C++ генерирует предупреждение, указывающее на потенциальную опасность потери данных при преобразовании из size_t (размер 8 байтов) в int (размер 4 байта). Но даже в 32-разрядных сборках, где size_t и int определяются компилятором Visual C++ как 32-битные целые, имеется несовпадению “без знака/со знаком”: size_t — беззнаковый тип, а int — со знаком. Это не проблема для строк разумной длины, но для гигантских строк с размером более 2 31 –1, т. е. длиной свыше двух миллиардов байтов, преобразование из целого без знака (size_t) в целое со знаком (int) может генерировать отрицательное число, а отрицательные длины не имеют смысла.

Поэтому вместо простого вызова utf8.length для получения длины входной строки UTF-8 и передачи этого значения в MultiByteToWideChar лучше проверить реальный размер size_t, убедившись, что преобразование в int будет безопасным и имеющим смысл, и только потом передавать его в MultiByteToWideChar.

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

Обратите внимание на использование шаблона класса std::numeric_limits (из стандартного заголовочного файла
в C++) для запроса наибольшего возможного значения для типа int. Однако этот код на самом деле может не скомпилироваться. В чем дело? Проблема в определении макросов min и max в заголовочных файлах Windows Platform SDK. В частности, специфичное для Windows определение макроса max препроцессора конфликтует с вызовом функции-члена std::numeric_limits ::max. Предотвратить это можно несколькими способами.

Возможное решение — указать #define NOMINMAX до включения . Это исключит определение специфичных для Windows макросов препроцессора min и max. Однако отсутствие определения этих макросов на самом деле может вызвать проблемы с другими заголовочными файлами Windows такими как , в котором нужны определения этих специфичных для Windows макросов.

Поэтому другой вариант — использовать дополнительную пару скобок вокруг вызова функции-члена std::numeric_limits::max, чтобы исключить раскрытие вышеупомянутых макросов:

Более того, в качестве альтернативы можно было бы использовать константу INT_MAX вместо C++-шаблона класса std::numeric_limits.

Какой бы подход вы ни выбрали, после проверки размера и анализа того, что длина значения корректна для переменной типа int, вы можете безопасно привести тип size_t к int, используя static_cast:

Заметьте, что длина строки UTF-8 измеряется восьмибитовыми единицами символов (char units), т. е. в байтах.

Первый API-вызов: получение длины конечной строки Теперь MultiByteToWideChar можно вызвать в первый раз, чтобы получить длину конечной строки UTF-16:

Заметьте, что функция вызывается с передачей нуля в качестве последнего аргумента. Это инструктирует MultiByteToWideChar просто вернуть необходимый размер строки назначения; на этом этапе никакое преобразование не выполняется. Также обратите внимание на то, что размер строки назначения выражается в wchar_t (не в восьмибитовых символах); это имеет смысл, так как строка назначения является Unicode-строкой в кодировке UTF-16, состоящей из последовательностей 16-битных wchar_t.

Чтобы получить доступ только для чтения к содержимому входной строки UTF-8 типа std::string, вызывается метод std::string::data. Поскольку длина строки UTF-8 явным образом передается как входной параметр, этот код будет работать и для экземпляров std::string со встроенными NUL.

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

Обработка ошибок Если предыдущий вызов функции закончился неудачей, например из-за наличия недопустимых для UTF-8 последовательностей во входной строке, то MultiByteToWideChar возвращает 0. В этом случае можно вызвать Win32-функцию GetLastError, чтобы выяснить детали о причине сбоя. Типичный код ошибки, возвращаемый в случае недопустимых для UTF-8 символов, — ERROR_NO_UNICODE_TRANSLATION.

При неудаче следует сгенерировать исключение. Им может быть экземпляр ранее определенного пользовательского класса Utf8ConversionException:

Выделение памяти для строки назначения Если вызов Win32-функции выполняется успешно, необходимая длина строки назначения содержится в локальной переменной utf16Length, поэтому можно выделить память под выходную строку UTF-16. Для строк UTF-16, Хранящихся в экземплярах класса std::wstring, достаточно вызова метода resize:

Заметьте: поскольку длина входной строки UTF-8 была явно передана в MultiByteToWideChar (вместо передачи –1 и запроса к этой API-функции на сканирование всей входной строки, пока не встретится завершающий NUL-символ), она не станет добавлять дополнительный завершающий NUL-символ к конечной строке. Эта API-функция будет просто обрабатывать точное количество символов во входной строке, указанное явно переданным значением длины. Следовательно, нет нужды вызывать std::wstring::resize со значением “utf16Length + 1” : так как дополнительный завершающий NUL-символ не дописывается в Win32 API, вам незачем оставлять пространство для него в строке назначения типа std::wstring (подробнее об этом см. всю ту же статью за июль 2015 года).

Второй API-вызов: выполнение преобразования Теперь, когда экземпляр UTF-16 wstring имеет достаточно пространства для конечного текста в кодировке UTF-16, можно вызвать MultiByteToWideChar во второй раз, чтобы получить реально преобразованные биты в строке назначения:

Обратите внимание на использование синтаксиса “&utf16[0]” , чтобы получить доступ для записи к внутреннему буферу памяти std::wstring (это тоже уже обсуждалось в статье за июль 2015 года).

Если первый вызов MultiByteToWideChar был успешен, вряд ли второй вызов потерпит неудачу. Тем не менее, проверка возвращаемого API-функцией значения определенно является хорошей практикой безопасного кодирования:

Иначе (в случае успеха) конечную строку UTF-16 можно наконец вернуть вызвавшему:

Пример использования Итак, если у вас есть Unicode-строка в кодировке UTF-8 (скажем, полученная от какого-то кросс-платформенного C++-кода) и вы хотите передать ее в Win32-функцию с поддержкой Unicode, то наша функция преобразования может быть вызвана так:

Функция Utf8ToUtf16 возвращает экземпляр wstring, содержащий строку UTF-16, и применительно к этому экземпляру вызывается метод c_str, чтобы получить исходный указатель в стиле C на строку, завершаемую символом NUL, которая передается в Win32-функции с поддержкой Unicode.

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

Библиотека преобразований Unicode-кодировок

В сопутствующий этой статье пакет исходного кода включен пример компилируемого C++-кода. Это повторно используемый код, без ошибок компилируемый в Visual C++ при уровне предупреждений 4 (/W4) в 32- и 64-разрядных сборках. Он реализован как библиотека C++ в виде только заголовочных файлов. По сути, этот модуль преобразования Unicode-кодировок состоит из двух заголовочных файлов: utf8except.h и utf8conv.h. Первый содержит определение C++-класса исключения, используемого для уведомления об ошибке при преобразованиях Unicode-кодировок. Второй реализует собственно функции преобразования Unicode-кодировок.

Заметьте, что utf8except.h содержит только кросс-платформенный C++-код; это делает возможным захват исключения при преобразовании кодировки UTF-8 в любых местах ваших проектов на C++, включая те части кода, которые не специфичны для Windows. Напротив, utf8conv.h содержит C++-код, специфичный для Windows, поскольку он напрямую взаимодействует с границей Win32 API.

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

Заключение

Unicode является стандартом де-факто для представления текста на любых языках в современном программном обеспечении. Unicode-текст можно кодировать в разнообразных форматах: два наиболее важных из них — UTF-8 и UTF-16. В C++-коде для Windows часто требуется преобразовывать строки между кодировками UTF-8 и UTF-16, так как Win32-функции с поддержкой Unicode используют UTF-16 в качестве “родной” Unicode-кодировки. Текст в кодировке UTF-8 удобно хранить в экземплярах STL-класса std::string, тогда как std::wstring хорошо подходит для хранения текста в кодировке UTF-16 в C++-коде для Windows, ориентированном на компилятор Visual C++.

Win32-функции MultiByteToWideChar и WideCharToMultiByte позволяют выполнять преобразования Unicode-текста между кодировками UTF-8 и UTF-16. Я подробно описал шаблон использования функции MultiByteToWideChar, обернув ее в повторно используемую вспомогательную функцию на современном C++ для выполнения преобразований из UTF-8 в UTF-16. Обратное преобразование следует очень похожему шаблону, и повторно используемый C++-код, реализующий его, доступен в пакете кода, сопутствующем этой статье.

Джованни Диканио (Giovanni Dicanio) — программист со специализацией в области C++ и Windows, автор Pluralsight и обладатель звания Visual C++ MVP. Помимо программирования и создания учебных курсов, с удовольствием помогает другим на форумах и в сообществах, преданных C++. С ним можно связаться по адресу giovanni.dicanio@gmail.com. Также ведет блог на blogs.msmvps.com/gdicanio.

Выражаю благодарность за рецензирование статьи экспертам Дэвиду Крейви (David Cravey) и Марку Грегуа (Marc Gregoire).

Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL