Как преобразовать строку на кириллице в кодировке 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 шт):

Автор решения: avp

По своим же комментариям набросал код

  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.

→ Ссылка
Автор решения: Kenny

Единственный способ который точно будет работать и на 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;
    }
→ Ссылка