§17 Строки широких символов. Локализация. Посимвольный анализ. Числовые преобразования

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

Unicode — это стандарт кодирования символов, позволяющий представить знаки почти всех письменных языков (как используемых, так и исторических). Юникод имеет несколько форм представления (англ. Unicode transformation format, UTF): UTF-8, UTF-16 и UTF-32. Кодировка utf-8 является одной из общепринятых и стандартизированных кодировок текста, которая позволяет хранить символы Юникода, используя переменное количество байт (от 1 до 6). Кодировка нашла широкое применение в UNIX-подобных операционных системах и в Интернете. Формат UTF-8 был изобретён 2 сентября 1992 года Кеном Томпсоном и Робом Пайком. Его преимущества перед другими кодировками в том, что в utf-8 осуществляется полная совместимость с символами таблицы ASCII имеющими десятичные коды 0 - 127, причем на эти символы отводится 8 бит (также как и в ASCII). В строках, где преобладает латиница, знаки пунктуации и пробел, использование кодировки utf-8 дает значительное сокращение информационного объема текста (в отличие от UTF-16, в MS Windows, в которой используется либо 16, либо 32 бита).
Начиная с версии Юникода 5.1 для кириллицы выделено четыре раздела в девятом блоке. Современный алфавит находится в первом разделе (и повторяет кодировку ISO 8859-5). Диапазон десятичных кодов: от 1040 (прописная буква 'А') до 1103 (строчная буква 'я'). Следующая программа может вывести весь кириллический сегмент Unicode:

Программа cpp-17.1
#include <iostream>
#include <locale>
#include <string>
#include <iomanip>
using namespace std;

int main() {
    locale::global(locale("ru_RU.UTF-8"));
    for (int i = 1024; i < 1279; i++) {
        wcout << static_cast<wchar_t>(i)
              << setw(3);
        if (i % 11 == 0)
            wcout << '\n';
    }
    wcout << endl;
    return 0;
}

Вывод

Ѐ  Ё  Ђ  Ѓ  Є  Ѕ  І  Ї  Ј  Љ  Њ  
Ћ  Ќ  Ѝ  Ў  Џ  А  Б  В  Г  Д  Е  
Ж  З  И  Й  К  Л  М  Н  О  П  Р  
С  Т  У  Ф  Х  Ц  Ч  Ш  Щ  Ъ  Ы  
Ь  Э  Ю  Я  а  б  в  г  д  е  ж  
з  и  й  к  л  м  н  о  п  р  с  
т  у  ф  х  ц  ч  ш  щ  ъ  ы  ь  
э  ю  я  ѐ  ё  ђ  ѓ  є  ѕ  і  ї  
ј  љ  њ  ћ  ќ  ѝ  ў  џ  Ѡ  ѡ  Ѣ  
ѣ  Ѥ  ѥ  Ѧ  ѧ  Ѩ  ѩ  Ѫ  ѫ  Ѭ  ѭ  
Ѯ  ѯ  Ѱ  ѱ  Ѳ  ѳ  Ѵ  ѵ  Ѷ  ѷ  Ѹ  
ѹ  Ѻ  ѻ  Ѽ  ѽ  Ѿ  ѿ  Ҁ  ҁ  ҂  ҃  ҄
  ҅  ҆  ҇      Ҋ  ҋ  Ҍ  ҍ  Ҏ  
ҏ  Ґ  ґ  Ғ  ғ  Ҕ  ҕ  Җ  җ  Ҙ  ҙ  
Қ  қ  Ҝ  ҝ  Ҟ  ҟ  Ҡ  ҡ  Ң  ң  Ҥ  
ҥ  Ҧ  ҧ  Ҩ  ҩ  Ҫ  ҫ  Ҭ  ҭ  Ү  ү  
Ұ  ұ  Ҳ  ҳ  Ҵ  ҵ  Ҷ  ҷ  Ҹ  ҹ  Һ  
һ  Ҽ  ҽ  Ҿ  ҿ  Ӏ  Ӂ  ӂ  Ӄ  ӄ  Ӆ  
ӆ  Ӈ  ӈ  Ӊ  ӊ  Ӌ  ӌ  Ӎ  ӎ  ӏ  Ӑ  
ӑ  Ӓ  ӓ  Ӕ  ӕ  Ӗ  ӗ  Ә  ә  Ӛ  ӛ  
Ӝ  ӝ  Ӟ  ӟ  Ӡ  ӡ  Ӣ  ӣ  Ӥ  ӥ  Ӧ  
ӧ  Ө  ө  Ӫ  ӫ  Ӭ  ӭ  Ӯ  ӯ  Ӱ  ӱ  
Ӳ  ӳ  Ӵ  ӵ  Ӷ  ӷ  Ӹ  ӹ  Ӻ  ӻ  Ӽ  
ӽ  Ӿ
Обратите внимание на то, что для преобразования используется специальный тип широких символов wchar_t.

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

Программа cpp-17.2

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

#include <iostream>
#include <locale>
#include <string>
using namespace std;

void myToUpper(wchar_t&);
bool myIsUpper(wchar_t&);
bool myCyr(wchar_t&);

int main() {
	locale::global(locale("ru_RU.UTF-8"));
	wstring myStr;
	wcout << L"Введите строку:\n";
	getline(wcin, myStr);
	for (auto &r : myStr)
		if (!myIsUpper(r) && myCyr(r))
			myToUpper(r);
	wcout << myStr << endl;
	return 0;
}
// меняет регистр символов
void myToUpper(wchar_t &ch) {
	ch = wchar_t(int(ch) - 32);
}
// проверка является ли символ в верхнем регистре
// true, если да
bool myIsUpper(wchar_t &ch) {
	return int(ch) > 1039 && int(ch) < 1072;
}
// проверка, что это кириллический символ Unicode
// true, если да
bool myCyr(wchar_t &ch) {
	return int(ch) > 1023 && int(ch) < 1280;
}

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

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

#include <locale>

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

locale::global(locale("ru_RU.UTF-8"));

Для работы с широкими символами существуют специальные версии типов и классов, которые используются с префиксом "w": wstring, wcout, wcin и т. п. Строка-литерал широких символов должна иметь суффикс "L". Рассмотрим пример использования строки широких символов.

Программа cpp-17.3

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

#include <iostream>
#include <string>
#include <locale>
#include <algorithm>
using namespace std;

int main() {
    locale::global(locale("ru_RU.UTF-8"));
    wstring G1;
    wchar_t G2;
    wcout << L"Введите строку G1:" << endl;
    getline(wcin, G1);
    wcout << L"Введите символ G2:" << endl;
    wcin >> G2;
    int i = 0;
    for (auto &s : G1) {
        if (s == G2) i++;
    }
    reverse(G1.begin(), G1.end());
    if (G1.find(G2) == string::npos)
        wcout << L"Такого символа нет!" << endl;
    else
        wcout << L"В строке найдено " << i
              << L" символов \"" << G2 << "\"" << endl;
    wcout << G1 << endl;
    return 0;
}

Вывод

Введите строку G1:
Это строка содержащая разные simvoli :))
Введите символ G2:
о
В строке найдено 3 символов о
)): ilovmis еынзар яащажредос акортс отЭ
Примечание. Метод find находит первую подстроку (в данном случае символа) в данной строке и возвращает позицию первого элемента подстроки. npos является константой класса string. Смысл выражения string::npos, в данном случае, означает признак конца строки. Подробно о методах класса string мы будем ниже.

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

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

Программа cpp-17.4
#include <iostream>
#include <string>
using namespace std;

int main() {
	string str1, str2;
	str1.reserve(10); // не обязательно
	str2.reserve(10);
	cout << "Введите строку 1:\n";
	getline(cin, str1);
	cout << "Введите строку 2:\n";
	getline(cin, str2);
	cout << str1 << " > " 
	     << str2 << " = "
	     << boolalpha;
	if (str1 > str2)
		cout << true << endl;
	else
		cout << false << endl;
	return 0;
}

Вывод

Введите строку 1:
moon
Введите строку 2:
sun
moon > sun = false

Посимвольная обработка строки

Для доступа к отдельному символу строки применяются либо операция обращения по индексу [] (например, myStr[i], как в C-массивах), либо метод at() (например, myStr.at(i)). При этом используется обычный цикл for. Если не предвидится изменение размера строки, то лучшим выбором будет не обычный for, а range-based for (программа cpp-17.3). Метод at() осуществляет контроль выхода за границы массива и если это произойдет, то будет сгенерировано исключение out_of_range. Следовательно, этому методу нужно отдавать предпочтение.
Фрагмент программы в которой производится обход символьного массива с помощью традиционного for и range-based for:

string S;
getline(cin, S);
int k = 0, m = 0;
for (size_t i = 0; i < S.size(); i++)
    if (S.at(i) == '\"') k++;
for (auto &r : S)
    if (r == '\?') m++;
Напоминаем, что двойная кавычка и вопросительный знак являются специальными символами, которые должны экранироваться символом '\' (управляющие последовательности).

Диапазонный цикл for (range-based for) нельзя использовать для массивов, которые в процессе обработки изменяют свой размер. Это объясняется тем, что этот вид цикла скрывает в себе работу с итераторами, а при изменении размера массива итератор конца становится недействительным.
Если используется заголовочный файл cctype (или cwctype для широких символов), то становятся доступными функции для работы с отдельными символами строки.

  • isalnum(), iswalnum() – проверяет, является ли символ буквенно-цифровым. Например: if (isalnum(r)) {...}
  • isalpha(), iswalpha() – проверяет, является ли символ буквенным
  • isdigit(), iswdigit() – проверяет, является ли символ цифрой
  • isxdigit(), iswxdigit() – проверяет, является ли символ шестнадцатеричной цифрой
  • iscntrl(), iswcntrl() – проверяет, является ли символ управляющим символом
  • isspace(), iswspace() – проверяет, является ли символ символом пробела
  • islower(), iswlower() – проверяет, является ли символ символом в нижнем регистре
  • isupper(), iswupper() – проверяет, является ли символ символом прописной буквы
  • ispunct(), iswpunct() – проверяет, является ли символ символом пунктуации
  • tolower(), towlower() – преобразует символ в нижний регистр. Например: r = tolower(r)
  • toupper(), towupper() – преобразует символ в верхний регистр
Аргументами функций библиотеки cwctype должны быть широкие символы
Программа cpp-17.5
#include <iostream>
#include <cwctype>
#include <string>
#include <locale>
using namespace std;

int main() {
	locale::global(locale("ru_RU.UTF-8"));
	int i = 0, j = 0;
	wstring myStr;
	wcout << L"Введите строку:\n";
	getline(wcin, myStr);
	for (auto &r : myStr) {
		if (iswalpha(r)) i++;
		if (iswspace(r)) j++;
		if (iswlower(r))
			r = towupper(r);
		else
			r = towlower(r);
	}
	wcout << L"В строке:\n"
		  << i << L" букв\n"
		  << j << L" пробелов\n"
		  "Новая строка:\n"
		  << myStr
		  << endl;
	return 0;
}

Вывод

Введите строку:
Введите строку:
в этой строке есть русские АБВГДЕЁЖ и английские ABCDEFGHIJ буквы
В строке:
56 букв
9 пробелов
Новая строка:
В ЭТОЙ СТРОКЕ ЕСТЬ РУССКИЕ абвгдеёж И АНГЛИЙСКИЕ abcdefghij БУКВЫ

Числовые преобразования

Для преобразования строковых значений в числа (и наоборот) существует набор функций преобразования. Преобразования объекта класса string осуществляются в знаковое целое, беззнаковое целое и в число с плавающей точкой. Функции числовых преобразований применяются для строк типов string и wstring (никаких дополнительных заголовков не требуется).

stoi, stol, stoll

Эти функции преобразуют строку в целое число со знаком. Функции имеет три аргумента:

stoi(str, pos, base)
stol(str, pos, base)
stoll(str, pos, base)
  • str – строка для преобразования
  • pos – адрес переменной целочисленного типа для сохранения в ней индекса первого непреобразованного символа. Необязательный аргумент
  • base – основание системы счисления. Необязательный аргумент
Для оснований больше, чем 10, набор корректных цифр дополняется символами латинского алфавита, начиная с Aa для системы счисления с основанием 11, до Zz для системы счисления с основанием 36. Регистр символов значения не имеет. Например:

string S1("45");
string S2("65");
int d = stoi(S1) + stoi(S2);
cout << d << endl;
stoul, stoull

Эти функции осуществляют аналогичные преобразования, но в беззнаковое целое.

stof, stod, stold

Эти функции преобразуют строку в число с плавающей точкой. Функции имеют два аргумента: str - строка для преобразования, pos - адрес переменной целочисленного типа для сохранения в ней индекса первого непреобразованного символа.

stof(str, pos)
stod(str, pos)
stold(str, pos)

Второй аргумент необязательный.

to_string, to_wstring

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

Программа cpp-17.6
#include <iostream>
#include <string>
using namespace std;

int main() {
	string S1 = "10000";
	string S2 = "10.2";
	cout << stoi(S1) * 2 << endl;
	cout << stod(S2) / 5 << endl;
	return 0;
}

Вывод

20000
2.04
Примеры решения задач
Вопросы
Темы сообщений
Задания А
Задания Б
Задания С
Ссылки
1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (Пока оценок нет)
Загрузка...
Print Friendly, PDF & Email

Обсуждение закрыто.