LINUX.ORG.RU

toupper<char32_t> выкидывает исключение std::bad_cast

 , , ,


0

1

Привет лор!

Возникла необходимость сравнивать русский текст регистронезависимо. Для перевода в utf32 нашел такую штуку: http://utfcpp.sourceforge.net/

И вроде она работает, но возникает проблема: при вызове boost::iequals(u32string, u32string) вызывается toupper<char32_t>, который выбрасывает std::bad_cast, и что с этим делать непонятно.

#include <string>
#include <boost/algorithm/string/predicate.hpp>
#include "utf8.h"

using namespace std;

inline bool reliable_iequals(const string &str1, const string &str2)
{
    u32string str1_32, str2_32;
    utf8::utf8to32(str1.begin(), str1.end(), back_inserter(str1_32));
    utf8::utf8to32(str2.begin(), str2.end(), back_inserter(str2_32));
    return boost::iequals(str1_32, str2_32);
}

int main()
{
    if(reliable_iequals("Одна строка", "ОДНА СтрОкА"))
        cout << "reliable_iequals работает.\n";
    else
        cout << "reliable_iequals не работает.\n";
    return 0;
}
terminate called after throwing an instance of 'std::bad_cast'
  what():  std::bad_cast

Система - Fedora 19 x86_64

locale
LANG=ru_RU.UTF-8
LC_CTYPE="ru_RU.UTF-8"
LC_NUMERIC="ru_RU.UTF-8"
LC_TIME="ru_RU.UTF-8"
LC_COLLATE="ru_RU.UTF-8"
LC_MONETARY="ru_RU.UTF-8"
LC_MESSAGES="ru_RU.UTF-8"
LC_PAPER="ru_RU.UTF-8"
LC_NAME="ru_RU.UTF-8"
LC_ADDRESS="ru_RU.UTF-8"
LC_TELEPHONE="ru_RU.UTF-8"
LC_MEASUREMENT="ru_RU.UTF-8"
LC_IDENTIFICATION="ru_RU.UTF-8"
LC_ALL=

★★★★★

бери icu или, как советовали выше, qtcore, который использует ту самую icu, но будет значительно проще в использовании

wota ★★ ()

Где setlocale? В какой кодировке строки в исходнике? Почему они без u8 префикса?

anonymous ()
Ответ на: комментарий от anonymous

Результат тот же, исходники в utf-8. Выбрасывает bad_cast.

#include <string>
#include <locale>
#include <boost/algorithm/string/predicate.hpp>
#include "utf8.h"

using namespace std;

inline bool reliable_iequals(const string &str1, const string &str2)
{
    u32string str1_32, str2_32;
    utf8::utf8to32(str1.begin(), str1.end(), back_inserter(str1_32));
    utf8::utf8to32(str2.begin(), str2.end(), back_inserter(str2_32));
    return boost::iequals(str1_32, str2_32);
}

int main()
{
    setlocale(LC_ALL,"ru_RU.UTF-8");
    if(reliable_iequals(u8"Одна строка", u8"ОДНА СтрОкА"))
        cout << "reliable_iequals работает.\n";
    else
        cout << "reliable_iequals не работает.\n";
    return 0;
}
Ivan_qrt ★★★★★ ()
Ответ на: комментарий от Ivan_qrt

но ИМХО это лютый костыль,

лютый костыль - это boost + utfcpp, а прямое решение - всегда хранить строки в icu::UnicodeString или QString и не знать геморроя с конвертацией, кодировками и операциями над строками

wota ★★ ()
Ответ на: комментарий от wota

прямое решение - всегда хранить строки в icu::UnicodeString или QString

Не спорю, с Qt проблем меньше. Но одна из поставленных мной целей была разобраться с stl.

Да и хранить вообще все строки в utf32 - это оверхед. По идее достаточно конвертировать в utf32 только то, что необходимо. Только вот это почему-то не работает.

В общем за совет спасибо, я им пользуюсь, но хотелось бы понять, в чём здесь проблема.

Ivan_qrt ★★★★★ ()
Ответ на: комментарий от Ivan_qrt

Но одна из поставленных мной целей была разобраться с stl

а причем тут STL? QString точно также предоставляет возможность работы через итераторы, а std::string, если что, не часть STL

wota ★★ ()
Ответ на: комментарий от wota

std::string, если что, не часть STL

o_O wikipedia с тобой не согласна, и говорит, что basic_string - часть stl. А std::string - это basic_string<char>. Но я имел ввиду, что это стандартное хранилище строк в STL/С++, с которым я хочу уметь работать. Ведь как-то же им люди пользуются.

Хотя я уже понимаю, почему столько альтернативных реализаций строк.

Ivan_qrt ★★★★★ ()
Ответ на: комментарий от wota

ICU кстати тоже позволяет бегать по строкам через итераторы

В icu я лезть пока что не хочу, то что Qt хорошо, я и так знаю.

Вопрос был не об этом, а почему std::toupper<char32_t> не работает.

Ivan_qrt ★★★★★ ()
Ответ на: комментарий от Ivan_qrt

В icu я лезть пока что не хочу

А придётся.

anonymous ()
Ответ на: комментарий от Ivan_qrt

o_O wikipedia с тобой не согласна, и говорит, что basic_string - часть stl.

либо ты неправильно понял, либо там написана чушь, это часть подбиблиотеки strings, в которую входят строки и regex, хотя в то же время basic_string может быть использована как контейнер STL и с ней можно работать соот-ми алгоритмами

Но я имел ввиду, что это стандартное хранилище строк в STL/С++, с которым я хочу уметь работать. Ведь как-то же им люди пользуются.

пользуются:

http://www.cplusplus.com/reference/locale/toupper/ http://www.cplusplus.com/reference/cwctype/towupper/

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

Хотя я уже понимаю, почему столько альтернативных реализаций строк.

если тебе надо действительно переносимый софт - просто бери Qt, там не только строки и гуй

wota ★★ ()
Ответ на: комментарий от Ivan_qrt

кстати по твоей оригинальной проблеме, в libstdc++ до сих пор нет <codecvt> и «Missing codecvt<char16_t> and codecvt<char32_t>», так что это недоработка gcc

wota ★★ ()
Последнее исправление: wota (всего исправлений: 1)
Ответ на: комментарий от wota

кстати по твоей оригинальной проблеме, в libstdc++ до сих пор нет <codecvt>

Собственно поэтому и стал использовать utfcpp. Он работает нормально (вроде).

По toupper: если использовать toupper(char32_variable) - то исключение не выкидывается, но для русских букв ничего не происходит. Если вызывать toupper(char32_variable, std::locale()) - то выкидывает bad_cast.

Ivan_qrt ★★★★★ ()
Ответ на: комментарий от iddqd

только я не понимаю зачем в utf32 перегонять

В utf8 символы непостоянной длинны, например английские буквы занимают 1 байт, а русские уже 2 байта. Пунктуация - 1 байт. string.at(i) возвращает i по счёту байт, соответственно в этом случае boost::iequals(u8str, u8str) для русского текста не работает. С utf32 все символы по 4 байта и string.at(i) возвращает 4 байта с кодом i-того символа и всё должно быть в порядке. Но toupper не работает.

Ivan_qrt ★★★★★ ()
Ответ на: комментарий от Ivan_qrt

С utf32 все символы по 4 байта

Матчасть подучи, не символы, а «codepoint». Который может, скажем, менять направление текста или быть модификатором.

например английские буквы занимают 1 байт, а русские уже 2 байта.

А если только эти два языка, тут не только utf16-[bl]e хватит, но и koi8r / iso8859-5 / etc.

anonymous ()
Ответ на: комментарий от anonymous

Матчасть подучи, не символы, а «codepoint»

Ок.

А если только эти два языка, тут не только utf16-[bl]e хватит, но и koi8r / iso8859-5 / etc.

С utf16 у toupper<char16_t> та же ошибка. Сейчас, кроме русского, мне ничего не надо, но интересует именно как поступать с utf8, а не с другими кодировками, т.к. потом может понадобиться.

Ivan_qrt ★★★★★ ()
Ответ на: комментарий от Ivan_qrt

интересует именно как поступать с utf8, а не с другими кодировками, т.к. потом может понадобиться.

юзать что-то несколько отличное от std::string и буста. Или жеж переизобретать юникодные реализации заново врукопашную.

arkhnchul ★★ ()
Ответ на: комментарий от arkhnchul

юзать что-то несколько отличное от std::string и буста.

Ну судя по всему так и придётся, т.к. ни в libstdc++, ни в libc++ (llvm) поддержка char32_t пока не реализована. Ну будем пользовать Qt.

Ivan_qrt ★★★★★ ()
Ответ на: комментарий от Ivan_qrt

Не, ну серьезно, а если придется добавлять потом другой язык?

Несколько сомнительно, что начальный способ учтет специфику вроде (icu - наверное умеет, не проверял, но в любом случае лучше велосипеда):

In standard German orthography, the sharp s («ß») is uppercased to a sequence of two capital S characters.

default mapping of «à» is «À», the uppercase conversion of «je vais à Paris» in some forms of French might be «JE VAIS A PARIS». Notice how the «à» is uppercased as «A» in this case.

// http://www.unicode.org/faq/casemap_charprop.html

anonymous ()

Вот такой код сравнивает русские строки регистронезависимо.

#include <string>
#include <cctype>
#include <iostream>

using namespace std;


bool compare(wstring, wstring);
    
int main()
{   
    
    setlocale(LC_CTYPE, "");
    wstring s1(L"Одна строка");
    wstring s2(L"ОДНА СтрОкА");
    if(compare(s1, s2))
        cout << "работает.\n";
    else
        cout << "не работает.\n";
    return 0;
}   
        
bool compare(wstring s1, wstring s2) 
{       
    if (s1.length() != s2.length())
        return 0; // not equal
    for (int i = 0; i < s1.length(); i++)
        if (towlower(s1[i]) != towlower(s2[i]))
            return 0; // not equal
    return 1; // strings are equal
}

anonymous ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.