Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.67/21: Рейтинг темы: голосов - 21, средняя оценка - 4.67
Любитель чаепитий
3744 / 1800 / 566
Регистрация: 24.08.2014
Сообщений: 6,016
Записей в блоге: 1
1

Корректен ли каст из string в wstring?

01.11.2016, 20:51. Показов 3835. Ответов 18
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Доброго вечера.
Вот кусок кода:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template< typename char_type >
string_t<char_type> make_string( const string_t<reverse_char_t<char_type>>& str )
{
    string_t<char_type> ret;
    for( auto it : str )
    {
        ret.push_back( it );
    }
    return ret;
}
 
template< typename char_type >
string_t<char_type> make_string( const string_t<char_type>& str )
{
    return str;
}
Коротко о шаблонах:
string_t<> - выбирает тип строки в зависимости от шаблона char_type, аналогично std::basic_string< char_type >.
reverse_char_t<> - выбирает противоположный тип символов, то есть если передать char, то заменит на wchar_t и наоборот.
В общем-то хотел узнать, корректно ли так кастовать?
Если будет wchar_t с какими-то символами, которые не влазят в char, то не будет ли это UB?
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
01.11.2016, 20:51
Ответы с готовыми решениями:

Каст this к std::string
Возможно ли скастовать указатель this к std::string так, что бы в std::string оказался адрес...

Из string в wstring
Как можно получить wstring-строку из string-строки? (символы в строке латинские, поэтому проблем...

Wstring to string как конвертировать
wstring to string как конвертировать Есть текст типа wstring s=&quot;fdgd123 2133&quot; как его переделать...

Из wchar_t* в wstring или в string
Как перевести из wchar_t* в wstring или в string ? Почему столько геморроя со строками... Есть...

18
230 / 113 / 79
Регистрация: 17.10.2016
Сообщений: 312
01.11.2016, 20:57 2
UB не будет. А вот потеря данных при преобразовании whar_t в char возможна.

Добавлено через 2 минуты
http://www.cplusplus.com/refer... /wcstombs/
1
Любитель чаепитий
3744 / 1800 / 566
Регистрация: 24.08.2014
Сообщений: 6,016
Записей в блоге: 1
01.11.2016, 21:05  [ТС] 3
Цитата Сообщение от John999 Посмотреть сообщение
А вот потеря данных при преобразовании whar_t в char возможна.
Это я знаю и это я готов терпеть.
0
7803 / 6567 / 2988
Регистрация: 14.04.2014
Сообщений: 28,706
02.11.2016, 09:27 4
Кодировка же от такого не преобразуется.
0
Любитель чаепитий
3744 / 1800 / 566
Регистрация: 24.08.2014
Сообщений: 6,016
Записей в блоге: 1
02.11.2016, 10:18  [ТС] 5
Цитата Сообщение от nmcf Посмотреть сообщение
Кодировка же от такого не преобразуется.
Я знаю, но мне этого и не нужно.
Суть в том, что я даю пользователю выбор, работать с char или wchar_t, но иногда мне надо кидать исключения с осмысленными значениями в переменных, поэтому требуется вставить содержимое этой строки в исключение, но т.к. не существует оператора + для разных типов строк, то пришлось идти на такую "хитрость".
0
Форумчанин
Эксперт CЭксперт С++
8216 / 5046 / 1437
Регистрация: 29.11.2010
Сообщений: 13,453
02.11.2016, 10:25 6
Как вариант, MultiByteToWideChar
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
std::wstring StrToWstr(const std::string &aString)
{
    // чтобы не делать каждый раз new создадим некоторый буфер заранее
    const int maxSize = 1024 * 1024; // 1Мб символов
    static wchar_t buff[maxSize]; 
 
    int nSize = ::MultiByteToWideChar(CP_UTF8, 0, aString.c_str(), static_cast<int>(aString.length() + 1), NULL, NULL);
 
    wchar_t *tBuff = buff;
    if (nSize > maxSize )
    {
        // пытаемся выделить память только если не хватило
        tBuff = new wchar_t[nSize];
    }
    if (nSize <= 0 && tBuff == NULL)
        return L"";
        
    ::MultiByteToWideChar(CP_UTF8, 0, aString.c_str(), static_cast<int>(aString.length() + 1), buff, nSize);
        
    std::wstring result(tBuff);
 
    if (tBuff != buff )
        delete[] tBuff;
 
    return result;
}
Добавлено через 2 минуты
Цитата Сообщение от GbaLog- Посмотреть сообщение
я даю пользователю выбор, работать с char или wchar_t
Не зная всей задачи, можно предположить, что не надо пользователю давать доступ к такой низкоуровневой части. Наоборот, желательно инкапсулировать тип хранения данных. Пусть работает с tstring, который может быть как wstring, так и string, в зависимости от условий задачи. И функции все написать под tstring.

Добавлено через 3 минуты
Либо такой вариант:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
std::string ws2s(const std::wstring& wstr, const std::locale& loc)
{
    if (wstr.empty())
        return std::string();
 
    typedef std::wstring::traits_type::state_type state_type;
    typedef std::codecvt<wchar_t, char, state_type> convert;
    
    const convert& cvt = std::use_facet<convert>(loc);
    std::string str(cvt.max_length()*wstr.size(), '\0');
    state_type state = state_type();
 
    const wchar_t* from_beg = &wstr[0];
    const wchar_t* from_end = from_beg + wstr.size();
    const wchar_t* from_nxt;
    char* to_beg = &str[0];
    char* to_end = to_beg + str.size();
    char* to_nxt;
    
    std::string::size_type sz = 0;
    std::codecvt_base::result r;
    do
    {
        r = cvt.out(state, from_beg, from_end, from_nxt,
                           to_beg,   to_end,   to_nxt);
        switch (r)
        {
        case std::codecvt_base::error:
            throw std::runtime_error("error converting wstring to string");
 
        case std::codecvt_base::partial:
            sz += to_nxt - to_beg;
            str.resize(2*str.size());
            to_beg = &str[sz];
            to_end = &str[0] + str.size();
            break;
 
        case std::codecvt_base::noconv:
            str.resize(sz + (from_end-from_beg)*sizeof(wchar_t));
            std::memcpy(&str[sz], from_beg,(from_end-from_beg)*sizeof(wchar_t));
            r = std::codecvt_base::ok;
            break;
 
        case std::codecvt_base::ok:
            sz += to_nxt - to_beg;
            str.resize(sz);
            break;
        }
    } while (r != std::codecvt_base::ok);
 
    return str;
}
Локаль в параметре можно сделать по-умолчанию std::locale("rus"), если речь идёт о винде.
0
Любитель чаепитий
3744 / 1800 / 566
Регистрация: 24.08.2014
Сообщений: 6,016
Записей в блоге: 1
02.11.2016, 10:41  [ТС] 7
Цитата Сообщение от MrGluck Посмотреть сообщение
MultiByteToWideChar
Не подойдёт, я же за кроссплатформу, а это WinAPI!
Цитата Сообщение от MrGluck Посмотреть сообщение
Не зная всей задачи, можно предположить, что не надо пользователю давать доступ к такой низкоуровневой части.
Может и не надо, я просто учился шаблонам и решил дать пользователю такую возможность.
Цитата Сообщение от MrGluck Посмотреть сообщение
Наоборот, желательно инкапсулировать тип хранения данных.
Он и инкапсулирован в string_t, всё внутри классов через них делается, пользователь волен только выбрать с какой кодировкой будет он работать с данным экземпляром класса.
Цитата Сообщение от MrGluck Посмотреть сообщение
в зависимости от условий задачи.
Это известно только пользователю, именно поэтому было решено так сделать.
Или Вы что-то другое имеете ввиду?
Цитата Сообщение от MrGluck Посмотреть сообщение
Либо такой вариант
Не понимаю, чем мой вариант не подходит?
0
Форумчанин
Эксперт CЭксперт С++
8216 / 5046 / 1437
Регистрация: 29.11.2010
Сообщений: 13,453
02.11.2016, 10:54 8
Цитата Сообщение от GbaLog- Посмотреть сообщение
Не понимаю, чем мой вариант не подходит?
Данные могут испортиться.
0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
02.11.2016, 11:02 9
Цитата Сообщение от GbaLog- Посмотреть сообщение
Не подойдёт, я же за кроссплатформу, а это WinAPI!
1) Взять std::wstring_convert из C++11. Если, конечно, в ваш C++11 положили codecvt (бывает и не кладут).
2) Взять преобразователь utf8->wchar_t из буста.
3) Взять готовый код.
4) Разобраться в структуре utf8, накатать свой конвертер. Ну, он конечно же не в три строчки пишется, но ничего сложного.
1
Форумчанин
Эксперт CЭксперт С++
8216 / 5046 / 1437
Регистрация: 29.11.2010
Сообщений: 13,453
02.11.2016, 11:07 10
Лучший ответ Сообщение было отмечено GbaLog- как решение

Решение

А вот каст string->wstring через codecvt
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
std::wstring s2ws(const std::string& str, const std::locale& loc)
{
    if (str.empty())
        return std::wstring();
 
    typedef std::string::traits_type::state_type state_type;
    typedef std::codecvt<wchar_t, char, state_type> convert;
    const convert& cvt = std::use_facet<convert>(loc);
    std::wstring wstr(str.size(), '\0');
    state_type state = state_type();
    const char* from_beg = &str[0];
    const char* from_end = from_beg + str.size();
    const char* from_nxt;
    wchar_t* to_beg = &wstr[0];
    wchar_t* to_end = to_beg + wstr.size();
    wchar_t* to_nxt;
    std::wstring::size_type sz = 0;
    std::codecvt_base::result r;
    do
    {
        r = cvt.in(state, from_beg, from_end, from_nxt,
                          to_beg,   to_end,   to_nxt);
        switch (r)
        {
        case std::codecvt_base::error:
            throw std::runtime_error("error converting string to wstring");
        case std::codecvt_base::partial:
            sz += to_nxt - to_beg;
            wstr.resize(2*wstr.size());
            to_beg = &wstr[sz];
            to_end = &wstr[0] + wstr.size();
            break;
        case std::codecvt_base::noconv:
            wstr.resize(sz + (from_end-from_beg));
            std::memcpy(&wstr[sz], from_beg, (std::size_t)(from_end-from_beg));
            r = std::codecvt_base::ok;
            break;
        case std::codecvt_base::ok:
            sz += to_nxt - to_beg;
            wstr.resize(sz);
            break;
        }
    } while (r != std::codecvt_base::ok);
 
   return wstr;
}
Цитата Сообщение от Renji Посмотреть сообщение
бывает и не кладут
Это проблема mingw. Вроде он так и не научился codecvt.
Есть ещё пятый вариант - iconv.
1
Любитель чаепитий
3744 / 1800 / 566
Регистрация: 24.08.2014
Сообщений: 6,016
Записей в блоге: 1
02.11.2016, 14:30  [ТС] 11
Цитата Сообщение от MrGluck Посмотреть сообщение
А вот каст string->wstring через codecvt
А вот такое какие подводные камни сулит, кроме эксепшонов при ?
У Вас какие-то лишние телодвижения, как мне кажется.
Хотя, скорее всего, я чего-то не понимаю просто.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template<class Facet>
struct deletable_facet : Facet
{
    template<class... Args>
    deletable_facet( Args&&... args ) 
                : Facet( std::forward<Args>(args)... ) {}
    ~deletable_facet() {}
};
...
std::wstring s2ws( std::string str )
{
    std::wstring ret = 
        std::wstring_convert<
            deletable_facet<std::codecvt<wchar_t, char, std::mbstate_t>>
                            >{}.from_bytes( str );
    return ret;
}
Ну и:
C++
1
2
3
4
5
6
std::string ws2s( std::wstring str )
{
    std::string ret = 
        std::wstring_convert<std::codecvt_utf8<wchar_t>>{}.to_bytes( str );
    return ret;
}
http://rextester.com/DZKGK99013
0
Форумчанин
Эксперт CЭксперт С++
8216 / 5046 / 1437
Регистрация: 29.11.2010
Сообщений: 13,453
02.11.2016, 14:39 12
Цитата Сообщение от GbaLog- Посмотреть сообщение
У Вас какие-то лишние телодвижения, как мне кажется.
Хотя, скорее всего, я чего-то не понимаю просто.
У меня сделано с учетом того, что текст может состоять из различных кодировок.
0
Любитель чаепитий
3744 / 1800 / 566
Регистрация: 24.08.2014
Сообщений: 6,016
Записей в блоге: 1
02.11.2016, 14:42  [ТС] 13
Цитата Сообщение от MrGluck Посмотреть сообщение
У меня сделано с учетом того, что текст может состоять из различных кодировок.
У меня в приложении, к счастью, поддерживается только латиница, отклонение от неё вызывает экзепшон.
Поэтому я, пожалуй, буду использовать свой вариант.
0
Форумчанин
Эксперт CЭксперт С++
8216 / 5046 / 1437
Регистрация: 29.11.2010
Сообщений: 13,453
02.11.2016, 15:25 14
Цитата Сообщение от GbaLog- Посмотреть сообщение
У меня в приложении, к счастью, поддерживается только латиница, отклонение от неё вызывает экзепшон.
Тогда зачем вообще нужен wchar_t? Все символы из US-ASCII влезут в байт (даже в полбайта). То есть любая ASCII-совместимая кодировка будет иметь одинаковые символы. И даже при преобразовании из UTF-8.
И действительно, при таком ограничении, никаких дополнительных алгоритмов кодировки не нужно, желательно лишь проверить, что код символа не выходит за старшие 4 бита (имеет код от 0 до 127).

Добавлено через 2 минуты
То есть вполне валидно даже делать так:
C++
1
2
3
std::string s = "Eniki beniki eli vareniki";
std::wstring ws(s.begin(), s.end());
std::string s2(ws.begin(), ws.end());
0
Любитель чаепитий
3744 / 1800 / 566
Регистрация: 24.08.2014
Сообщений: 6,016
Записей в блоге: 1
02.11.2016, 15:37  [ТС] 15
Цитата Сообщение от MrGluck Посмотреть сообщение
Тогда зачем вообще нужен wchar_t?
Цитата Сообщение от GbaLog- Посмотреть сообщение
Может и не надо, я просто учился шаблонам и решил дать пользователю такую возможность.
На самом деле я и сам думал о том, что мне не нужен этот wchar_t.
Но для обучения решил сделать и с ним.

Добавлено через 47 секунд
Цитата Сообщение от MrGluck Посмотреть сообщение
То есть вполне валидно даже делать так
Пока останется, как есть, дальше виднее будет.
0
Форумчанин
Эксперт CЭксперт С++
8216 / 5046 / 1437
Регистрация: 29.11.2010
Сообщений: 13,453
02.11.2016, 15:54 16
Цитата Сообщение от GbaLog- Посмотреть сообщение
Но для обучения решил сделать и с ним.
Можно, как вариант, ввести свой тип - параметр char_traits от basic_string, который позволил бы осуществлять регистронезависимое сравнение и поиск в словах. Тут хоть будет профит виден, а не привязка реализации к условию задачи.
У меня, если что, есть уже готовая реализация, но я думаю, что тебе самому будет интересно её написать. В крайнем случае, могу выложить.
0
Любитель чаепитий
3744 / 1800 / 566
Регистрация: 24.08.2014
Сообщений: 6,016
Записей в блоге: 1
02.11.2016, 16:52  [ТС] 17
MrGluck,
Это Вы мне типа задание дали?
Ладно.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <string>
#include <iostream>
/////////////////////////////////////////////////////////////////////////////////////////
struct my_char_traits : std::char_traits<char>
{
    //-----------------------------------------------------------------------------------
    static bool eq( const char_type a, const char_type b )
    {
        return std::tolower( a ) == std::tolower( b );
    }
    //-----------------------------------------------------------------------------------
    static bool lt( const char_type a, const char_type b )
    {
        return std::tolower( a ) <  std::tolower( b );
    }
    //-----------------------------------------------------------------------------------
    static int compare( const char_type* str1, const char* str2, size_t count )
    {
        for ( size_t it{}; it < count; ++it )
            if (lt( std::tolower( str1[it] ), std::tolower( str2[it] ) ) )
                return -1;
            else if ( lt( std::tolower( str2[it] ), std::tolower( str1[it] ) ) )
                return 1;
      return 0;
    }
    //-----------------------------------------------------------------------------------
    static const char_type* find( const char_type* str, size_t count, const char_type ch )
    {
        for( size_t it{}; it < count; ++it )
        {
            if( eq( std::tolower( str[it] ), std::tolower( ch ) ) )
            {
                return str + it;
            }
        }
        return nullptr;
    }
    //-----------------------------------------------------------------------------------
};
/////////////////////////////////////////////////////////////////////////////////////////
using my_string = std::basic_string<char, my_char_traits>;
/////////////////////////////////////////////////////////////////////////////////////////
int main()
{
    my_string str1 = "Hello";
    my_string str2 = "hELLO";
    std::cout << std::boolalpha << ( str1 == str2 ) << std::endl;
}
0
Форумчанин
Эксперт CЭксперт С++
8216 / 5046 / 1437
Регистрация: 29.11.2010
Сообщений: 13,453
02.11.2016, 17:36 18
Цитата Сообщение от GbaLog- Посмотреть сообщение
типа задание дали?
Подбросил идею как можно разнообразить строки и поработать с шаблонами, не меняя char_type.
Но раз уж код выложен.. В compare и find можно уже не приводить к tolower. И find вроде вообще не нужен.
0
Любитель чаепитий
3744 / 1800 / 566
Регистрация: 24.08.2014
Сообщений: 6,016
Записей в блоге: 1
02.11.2016, 17:47  [ТС] 19
Цитата Сообщение от MrGluck Посмотреть сообщение
В compare и find можно уже не приводить к tolower. И find вроде вообще не нужен.
А точно, я же уже в функциях всё написал, ладно, тут я ошибся немного.
0
02.11.2016, 17:47
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
02.11.2016, 17:47
Помогаю со студенческими работами здесь

Перевод из string в wstring. Неправильная кодировка
Здорова господа! Перевожу строку из string в wstring, в строке русские символы и они выводятся в...

По поводу wchar_t, wstring, char, string и кодировок
Здравствуйте, пишу консольную либу которая будет работать с большим количеством символов в разных...

Не могу поместить в переменную wstring и string русское слово
Помогите пожалуйста , когда ввожу с клавиатуры в переменную string или wstring русское слово , то...

Как правильно перевести std::wstring в std::string ?
Собственно как? :)


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
19
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru