Страница 1 из 2

Как преобразовать строку std::u32string в нижний регистр?

Добавлено: 26 дек 2015, 20:14
Сионист
Сохранив оригинал строки.

Re: Как преобразовать строку std::u32string в нижний регистр?

Добавлено: 26 дек 2015, 20:45
Romeo
Если оригинал нужно сохранить, то придётся предварительно откопировать строку.

Для преобразования в нижний регистр можно воспользоваться функцией tolower из пространства имён std, применив её к каждому символу строки. В С++ (в отличии от С), есть перегруженный вариант этой функции, принимающий в качестве дополнительного параметра локаль (locale). Он-то нам и нужен. Само собой, локаль должна быть Russian, если хочешь, чтобы понижение регистра работало и для русских символов.

Небольшая заметка, касающаяся конкретно MinGW. В 32-битной версии нет стандартной русской локали. Мне пришлось руками написать фасет (facet) ctype, и проставить его в стандартную сишную локаль, чтобы алгоритм заработал для русских символов. Судя по форумам, знающие люди пишут, что в 64-битной версии компилятора этот недочёт устранён. Я ещё это не проверял, но так как у тебя именно такой компилятор, то можешь попробовать и рассказать нам получилось ли.

Добавлено: 27 дек 2015, 08:59
Сионист
Romeo писал(а):если хочешь, чтобы понижение регистра работало и для русских символов.
Как раз только их преобразование и обязательно.
Romeo писал(а):принимающий в качестве дополнительного параметра локаль (locale). Он-то нам и нужен. Само собой, локаль должна быть Russian
Говорит, что
'Russian' was not declared in this scope|
.

Re: Как преобразовать строку std::u32string в нижний регистр?

Добавлено: 27 дек 2015, 09:51
Romeo
Покажи, как локаль создал. Такое ощущение, что Russian ты пытаешься использовать, как идентификатор.

Добавлено: 28 дек 2015, 13:20
Сионист
Romeo писал(а):Покажи, как локаль создал. Такое ощущение, что Russian ты пытаешься использовать, как идентификатор.
Ну да. А как надо?
Romeo писал(а):Мне пришлось руками написать фасет (facet) ctype, и проставить его в стандартную сишную локаль
Что это такое и как его писать?

Re: Как преобразовать строку std::u32string в нижний регистр?

Добавлено: 28 дек 2015, 14:22
Romeo
Сионист писал(а):Ну да. А как надо?
Судя по форумам, на MinGW 64 должно работать вот так:

Код: Выделить всё

std::wstring ws(L"some Русский текст");
std::locale loc("Russian");
std::transform(ws.begin(), ws.end(), ws.begin(), std::bind2nd(std: :p tr_fun(&std::tolower<wchar_t>), loc));
Так же есть люди, которые говорят, что вместо "Russian" нужно/можно писать "ru_RU.UTF-8".

Но мне на практике проверить не удалось, так как MinGW 32, которым я пользуюсь, не поддерживает русскую кодировку (кидает исключение в конструкторе std::locale, если передать что-то, кроме пустой строки). Мне пришлось пойти окольными путями.
Сионист писал(а):Что это такое и как его писать?
Как написать фасет расскажу, если попробуешь со стандартной локалью, и у тебя ничего не получится. А вообще, этот вопрос гуглится с полпинка. Начинать нужно с конструктора locale, который принимает оригинальную локаль и facet.

Re: Как преобразовать строку std::u32string в нижний регистр?

Добавлено: 28 дек 2015, 14:56
Сионист
Аварийное завершение с зелёным прогрессбаром.

Re: Как преобразовать строку std::u32string в нижний регистр?

Добавлено: 28 дек 2015, 15:24
Romeo
Ты c "ru_RU.UTF-8" тоже пробовал запускать?

А если в try взять и сделать catch(...), тогда что?

Подозреваю, что летит эксепшн из конструктора std::locale. И, если так, то тогда врут на форуме, что на MinGW 64 всё исправлено. Тогда нужно свой фасет делать. Можешь мой взять (см. следующие 2 сообщения, по лимиту символов не влазит в это).

Проставляется этот фасет в локаль вот таким образом:

Код: Выделить всё

russian_ctype rct;
std::locale loc(std::locale(), &rct);
Будь осторожней, если собираешься возвращаться локаль из функции или делать её статической. Дело в том, что она не владеет фасетами, а лишь содержит поинтеры на них (так стандартом положено). Так что если локаль будешь делать статической, чтобы использовать во всём проекте одну, то фасет тоже статическим делай.

Re: Как преобразовать строку std::u32string в нижний регистр?

Добавлено: 28 дек 2015, 15:25
Romeo

Код: Выделить всё

      class russian_ctype : public std::ctype<wchar_t>
      {
         using _base = std::ctype<wchar_t>;
      
      public:
         russian_ctype() = default;

      protected:
         virtual wchar_t do_toupper(wchar_t __c) const override
         {
            using toupper_vec_base = std::vector<std: :p air<wchar_t, wchar_t>>;
      
            static class toupper_vec : public toupper_vec_base
            {
            public:
               toupper_vec() : toupper_vec_base()
               {
                  reserve(33);
                  push_back(std::make_pair(L'а', L'А'));
                  push_back(std::make_pair(L'б', L'Б'));
                  push_back(std::make_pair(L'в', L'В'));
                  push_back(std::make_pair(L'г', L'Г'));
                  push_back(std::make_pair(L'д', L'Д'));
                  push_back(std::make_pair(L'е', L'Е'));
                  push_back(std::make_pair(L'ё', L'Ё'));
                  push_back(std::make_pair(L'ж', L'Ж'));
                  push_back(std::make_pair(L'з', L'З'));
                  push_back(std::make_pair(L'и', L'И'));
                  push_back(std::make_pair(L'й', L'Й'));
                  push_back(std::make_pair(L'к', L'К'));
                  push_back(std::make_pair(L'л', L'Л'));
                  push_back(std::make_pair(L'м', L'М'));
                  push_back(std::make_pair(L'н', L'Н'));
                  push_back(std::make_pair(L'о', L'О'));
                  push_back(std::make_pair(L'п', L'П'));
                  push_back(std::make_pair(L'р', L'Р'));
                  push_back(std::make_pair(L'с', L'С'));
                  push_back(std::make_pair(L'т', L'Т'));
                  push_back(std::make_pair(L'у', L'У'));
                  push_back(std::make_pair(L'ф', L'Ф'));
                  push_back(std::make_pair(L'х', L'Х'));
                  push_back(std::make_pair(L'ц', L'Ц'));
                  push_back(std::make_pair(L'ч', L'Ч'));
                  push_back(std::make_pair(L'ш', L'Ш'));
                  push_back(std::make_pair(L'щ', L'Щ'));
                  push_back(std::make_pair(L'ъ', L'Ъ'));
                  push_back(std::make_pair(L'ы', L'Ы'));
                  push_back(std::make_pair(L'ь', L'Ь'));
                  push_back(std::make_pair(L'э', L'Э'));
                  push_back(std::make_pair(L'ю', L'Ю'));
                  push_back(std::make_pair(L'я', L'Я'));

                  // Сортировка необходима из-за нестандартного положения буквы 'ё' в таблице символов
                  std::sort(begin(), end(), [](const_reference ref1, const_reference ref2) -> bool
                  {
                     return (ref1.first < ref2.first);
                  });
               }
            } vec;
      
            auto it = std::lower_bound(vec.begin(), vec.end(), __c, 
               [](toupper_vec::const_reference ref, wchar_t __c) -> bool
            {
               return (ref.first < __c);
            });
      
            if (it != vec.end() && it->first == __c)
            {
               return it->second;
            }
      
            return _base::do_toupper(__c);
         }
      
         virtual const wchar_t* do_toupper(wchar_t* __lo, const wchar_t* __hi) const override
         {
            for (wchar_t* curr = __lo; curr != __hi; ++curr)
            {
               *curr = do_toupper(*curr);
            }
            return __lo;
         }

Re: Как преобразовать строку std::u32string в нижний регистр?

Добавлено: 28 дек 2015, 15:25
Romeo

Код: Выделить всё

      
         virtual wchar_t do_tolower(wchar_t __c) const override
         {
            using tolower_vec_base = std::vector<std: :p air<wchar_t, wchar_t>>;

            static class tolower_vec : public tolower_vec_base
            {
            public:
               tolower_vec() : tolower_vec_base()
               {
                  reserve(33);
                  push_back(std::make_pair(L'А', L'а'));
                  push_back(std::make_pair(L'Б', L'б'));
                  push_back(std::make_pair(L'В', L'в'));
                  push_back(std::make_pair(L'Г', L'г'));
                  push_back(std::make_pair(L'Д', L'д'));
                  push_back(std::make_pair(L'Е', L'е'));
                  push_back(std::make_pair(L'Ё', L'ё'));
                  push_back(std::make_pair(L'Ж', L'ж'));
                  push_back(std::make_pair(L'З', L'з'));
                  push_back(std::make_pair(L'И', L'и'));
                  push_back(std::make_pair(L'Й', L'й'));
                  push_back(std::make_pair(L'К', L'к'));
                  push_back(std::make_pair(L'Л', L'л'));
                  push_back(std::make_pair(L'М', L'м'));
                  push_back(std::make_pair(L'Н', L'н'));
                  push_back(std::make_pair(L'О', L'о'));
                  push_back(std::make_pair(L'П', L'п'));
                  push_back(std::make_pair(L'Р', L'р'));
                  push_back(std::make_pair(L'С', L'с'));
                  push_back(std::make_pair(L'Т', L'т'));
                  push_back(std::make_pair(L'У', L'у'));
                  push_back(std::make_pair(L'Ф', L'ф'));
                  push_back(std::make_pair(L'Х', L'х'));
                  push_back(std::make_pair(L'Ц', L'ц'));
                  push_back(std::make_pair(L'Ч', L'ч'));
                  push_back(std::make_pair(L'Ш', L'ш'));
                  push_back(std::make_pair(L'Щ', L'щ'));
                  push_back(std::make_pair(L'Ъ', L'ъ'));
                  push_back(std::make_pair(L'Ы', L'ы'));
                  push_back(std::make_pair(L'Ь', L'ь'));
                  push_back(std::make_pair(L'Э', L'э'));
                  push_back(std::make_pair(L'Ю', L'ю'));
                  push_back(std::make_pair(L'Я', L'я'));

                  // Сортировка необходима из-за нестандартного положения буквы 'ё' в таблице символов
                  std::sort(begin(), end(), [](const_reference ref1, const_reference ref2) -> bool
                  {
                     return (ref1.first < ref2.first);
                  });
               }
            } vec;
      
            auto it = std::lower_bound(vec.begin(), vec.end(), __c, 
               [](tolower_vec::const_reference ref, wchar_t __c) -> bool
            {
               return (ref.first < __c);
            });
      
            if (it != vec.end() && it->first == __c)
            {
               return it->second;
            }
      
            return _base::do_tolower(__c);
         }
      
         virtual const wchar_t* do_tolower(wchar_t* __lo, const wchar_t* __hi) const override
         {
            for (wchar_t* curr = __lo; curr != __hi; ++curr)
            {
               *curr = do_tolower(*curr);
            }
            return __lo;
         }
      };