Как преобразовать строку на кириллице в кодировке utf8 в нижний регистр на C++ средствами std?
Этот код не работает с кириллицей в кодировке utf8
std::string s{"Тест"};
std::transform(s.begin(), s.end(), s.begin(), [](char c) -> char {
return std::tolower(c, std::locale());
});
std::cout<<s<<std::endl;
Ответы (2 шт):
По своим же комментариям набросал код
char *s = 0;
size_t n = 0;
while (getline(&s, &n, stdin) >= 0) {
for (unsigned char *p = s; *p; p++) {
if (p[0] == 0xD0) {
if (0x90 <= p[1] && p[1] <= 0x9F)
p[1] += 32;
else if (0xA0 <= p[1] && p[1] <= 0xAF)
p[0] += 1, p[1] &= ~0x30;
else if (p[1] == 0x81)
p[0] = 0xD1, p[1] = 0x91;
}
}
puts(s);
}
Жаль, длинноват для комментария, но может кому-нибудь пригодится.
Тогда уж, перенесу сюда и комментарий, поясняющий его.
Сам алгоритм прост. Достаточно посмотреть на любую таблицу кириллицы в utf-8.
Легко заметить, что у всех букв А-П первый байт 0xD0, а второй от 0x90 до 0x9F. Для перевода их в lowcase (а-п) достаточно прибавить 32 ко второму байту.
У букв Р-Я первый байт также 0xD0, а второй от 0xA0 до 0xAF. Для их перевода надо добавить 1 к первому байту, а во втором сбросить биты 4 и 5 (т.е. выполнить chars[i + 1] &= ~0x30, если i это текущий индекс первого байта рассматриваемого символа в utf-8).
Отдельно надо проверить Ё (коды 2-х байт 0xD081). Ее байты проще всего просто заменить на 0xD191.
Единственный способ который точно будет работать и на Windows и на Linux, это использовать wchar_t вместо char. А так как мы живем во времена когда у С++ появилась filesystem и std::filesystem::path то преобразовывать строки из char в wchar_t можно через path.
А вот собственно и функция:
#include <string>
#include <filesytem>
void ToLower(std::string string) {
std::wstring ws = std::filesystem::path(string).native();
std::wstring outWs;
std::string outS;
for (wchar_t wc : ws) {
unsigned int c = (unsigned int)wc;
switch (c) {
case L'A': outWs += 'a'; break;
case L'B': outWs += 'b'; break;
case L'C': outWs += 'c'; break;
case L'D': outWs += 'd'; break;
case L'E': outWs += 'e'; break;
case L'F': outWs += 'f'; break;
case L'G': outWs += 'g'; break;
case L'H': outWs += 'h'; break;
case L'I': outWs += 'i'; break;
case L'J': outWs += 'j'; break;
case L'K': outWs += 'k'; break;
case L'L': outWs += 'l'; break;
case L'M': outWs += 'm'; break;
case L'N': outWs += 'n'; break;
case L'O': outWs += 'o'; break;
case L'P': outWs += 'p'; break;
case L'Q': outWs += 'q'; break;
case L'R': outWs += 'r'; break;
case L'S': outWs += 's'; break;
case L'T': outWs += 't'; break;
case L'U': outWs += 'u'; break;
case L'V': outWs += 'v'; break;
case L'W': outWs += 'w'; break;
case L'X': outWs += 'x'; break;
case L'Y': outWs += 'y'; break;
case L'Z': outWs += 'z'; break;
case L'А': outWs += L'а'; break;
case L'Б': outWs += L'б'; break;
case L'В': outWs += L'в'; break;
case L'Г': outWs += L'г'; break;
case L'Д': outWs += L'д'; break;
case L'Е': outWs += L'е'; break;
case L'Ж': outWs += L'ж'; break;
case L'З': outWs += L'з'; break;
case L'И': outWs += L'и'; break;
case L'Й': outWs += L'й'; break;
case L'К': outWs += L'к'; break;
case L'Л': outWs += L'л'; break;
case L'Н': outWs += L'н'; break;
case L'М': outWs += L'м'; break;
case L'О': outWs += L'о'; break;
case L'П': outWs += L'п'; break;
case L'Р': outWs += L'р'; break;
case L'С': outWs += L'с'; break;
case L'Т': outWs += L'т'; break;
case L'У': outWs += L'у'; break;
case L'Ф': outWs += L'ф'; break;
case L'Х': outWs += L'х'; break;
case L'Ц': outWs += L'ц'; break;
case L'Ч': outWs += L'ч'; break;
case L'Ш': outWs += L'ш'; break;
case L'Щ': outWs += L'щ'; break;
case L'Ь': outWs += L'ь'; break;
case L'Ъ': outWs += L'ъ'; break;
case L'Ы': outWs += L'ы'; break;
case L'Э': outWs += L'э'; break;
case L'Ю': outWs += L'ю'; break;
case L'Я': outWs += L'я'; break;
default: {
outWs += wc; break;
}
}
}
string = std::filesystem::path(outWs).string();
}
int main() {
std::string s = "Съешь Ещё Этих Мягких Французских Булок Да Выпей Чаю";
ToLower(s);
std::cout << s << std::end;
return 0;
}