Эквивалент mbsrtowcs и wcsrtombs в C ++ с использованием локалей и потоков

Существует ли в C ++ эквивалент функций типа mbsrtowcs и wcsrtombs, использующий функции std :: locale и потоков C ++?

Я пытаюсь найти лучший способ преобразования между std :: string и std :: wstring, используя стандартную библиотеку. Кажется, что std :: locale почти может это сделать, но я немного сомневаюсь в некоторых деталях или в том, какие ограничения у него могут быть.

Некоторые особенности: я использую Linux, который использует utf-8 в качестве собственной кодировки. Я хотел бы перейти от utf-8 std :: string к std :: wstring и обратно без потери информации.

Я думаю, что в Windows могут быть некоторые ограничения для локалей, но меня это не особо беспокоит. Пока ответ работает в Linux и не имеет никаких зависимостей, кроме libstdc ++, то есть никакой зависимости от ускорения, я счастлив.

Ссылки на справочную информацию приветствуются.

ПРИМЕЧАНИЕ. Кажется, есть некоторая путаница. Более одного символа могут представлять один символ в UTF-8, поэтому функции, которые не учитывают это при преобразовании из wchar_t в char, не будут работать.


person catphive    schedule 28.09.2010    source источник


Ответы (2)


locale является излишним для этой задачи - UTF-8 и UTF-16 могут быть преобразованы туда и обратно с помощью простых двоичных преобразований. Вот код, основанный на моем ответе на предыдущий вопрос..

std::string UTF16to8(const wchar_t * in)
{
    std::string out;
    if (in == NULL)
        return out;

    unsigned int codepoint = 0;
    for (in;  *in != 0;  ++in)
    {
        if (*in >= 0xd800 && *in <= 0xdbff)
            codepoint = ((*in - 0xd800) << 10) + 0x10000;
        else
        {
            if (*in >= 0xdc00 && *in <= 0xdfff)
                codepoint |= *in - 0xdc00;
            else
                codepoint = *in;

            if (codepoint <= 0x7f)
                out.append(1, static_cast<char>(codepoint));
            else if (codepoint <= 0x7ff)
            {
                out.append(1, static_cast<char>(0xc0 | ((codepoint >> 6) & 0x1f)));
                out.append(1, static_cast<char>(0x80 | (codepoint & 0x3f)));
            }
            else if (codepoint <= 0xffff)
            {
                out.append(1, static_cast<char>(0xe0 | ((codepoint >> 12) & 0x0f)));
                out.append(1, static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f)));
                out.append(1, static_cast<char>(0x80 | (codepoint & 0x3f)));
            }
            else
            {
                out.append(1, static_cast<char>(0xf0 | ((codepoint >> 18) & 0x07)));
                out.append(1, static_cast<char>(0x80 | ((codepoint >> 12) & 0x3f)));
                out.append(1, static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f)));
                out.append(1, static_cast<char>(0x80 | (codepoint & 0x3f)));
            }
            codepoint = 0;
        }
    }
    return out;
}

std::wstring UTF8to16(const char * in)
{
    std::wstring out;
    if (in == NULL)
        return out;

    unsigned int codepoint = 0;
    int following = 0;
    for (in;  *in != 0;  ++in)
    {
        unsigned char ch = *in;
        if (ch <= 0x7f)
        {
            codepoint = ch;
            following = 0;
        }
        else if (ch <= 0xbf)
        {
            if (following > 0)
            {
                codepoint = (codepoint << 6) | (ch & 0x3f);
                --following;
            }
        }
        else if (ch <= 0xdf)
        {
            codepoint = ch & 0x1f;
            following = 1;
        }
        else if (ch <= 0xef)
        {
            codepoint = ch & 0x0f;
            following = 2;
        }
        else
        {
            codepoint = ch & 0x07;
            following = 3;
        }
        if (following == 0)
        {
            if (codepoint > 0xffff)
            {
                out.append(1, static_cast<wchar_t>(0xd800 + (codepoint >> 10)));
                out.append(1, static_cast<wchar_t>(0xdc00 + (codepoint & 0x03ff)));
            }
            else
                out.append(1, static_cast<wchar_t>(codepoint));
            codepoint = 0;
        }
    }
    return out;
}

Вот версия (непроверенная) для использования, если ваш wchar_t 32-битный, а не 16-битный.

std::string UTF32to8(const wchar_t * in)
{
    assert(sizeof(wchar_t) >= 4);
    std::string out;
    if (in == NULL)
        return out;

    for (in;  *in != 0;  ++in)
    {
        unsigned int codepoint = *in;

        if (codepoint <= 0x7f)
            out.append(1, static_cast<char>(codepoint));
        else if (codepoint <= 0x7ff)
        {
            out.append(1, static_cast<char>(0xc0 | ((codepoint >> 6) & 0x1f)));
            out.append(1, static_cast<char>(0x80 | (codepoint & 0x3f)));
        }
        else if (codepoint <= 0xffff)
        {
            out.append(1, static_cast<char>(0xe0 | ((codepoint >> 12) & 0x0f)));
            out.append(1, static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f)));
            out.append(1, static_cast<char>(0x80 | (codepoint & 0x3f)));
        }
        else
        {
            out.append(1, static_cast<char>(0xf0 | ((codepoint >> 18) & 0x07)));
            out.append(1, static_cast<char>(0x80 | ((codepoint >> 12) & 0x3f)));
            out.append(1, static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f)));
            out.append(1, static_cast<char>(0x80 | (codepoint & 0x3f)));
        }
    }
    return out;
}

std::wstring UTF8to32(const char * in)
{
    assert(sizeof(wchar_t) >= 4);
    std::wstring out;
    if (in == NULL)
        return out;

    wchar_t codepoint = 0;
    int following = 0;
    for (in;  *in != 0;  ++in)
    {
        unsigned char ch = *in;
        if (ch <= 0x7f)
        {
            codepoint = ch;
            following = 0;
        }
        else if (ch <= 0xbf)
        {
            if (following > 0)
            {
                codepoint = (codepoint << 6) | (ch & 0x3f);
                --following;
            }
        }
        else if (ch <= 0xdf)
        {
            codepoint = ch & 0x1f;
            following = 1;
        }
        else if (ch <= 0xef)
        {
            codepoint = ch & 0x0f;
            following = 2;
        }
        else
        {
            codepoint = ch & 0x07;
            following = 3;
        }
        if (following == 0)
        {
            out.append(1, codepoint);
            codepoint = 0;
        }
    }
    return out;
}
person Mark Ransom    schedule 28.09.2010
comment
wstrings не UTF-16 в Linux. - person caf; 28.09.2010
comment
@caf, я этого не знал. Но UCS-32 еще проще. - person Mark Ransom; 28.09.2010
comment
Обратите внимание, что правильные декодеры UTF-8 должны отклонять недопустимые последовательности (например, слишком длинные кодировки, такие как C0 A0) - это обязательно, потому что исторически было возможно проникнуть злонамеренный ввод через неканонические кодировки. - person caf; 28.09.2010
comment
@caf, очень хорошее замечание. Но не кажется ли вам, что дезинфекцию лучше сделать отдельным шагом? - person Mark Ransom; 28.09.2010
comment
Что ж, теперь мы говорим о философии;) - но я склонен не соглашаться, потому что 1) очистка требует реализации того же конечного автомата, что и декодирование; и 2) который вводит возможность ошибок TOCTTOU. - person caf; 29.09.2010
comment
Всякий раз, когда я вижу, как люди публикуют свои собственные процедуры преобразования UTF-8, во мне вспыхивают жестокие чувства. - person paniq; 18.08.2011

Вы пробовали создать какую-нибудь простую функцию?

std::wstring StringToWString(const std::string& src)
{
 std::wstring str(src.length(),L' ');
 std::copy(src.begin(), src.end(), str.begin());
 return str; 
}


std::string WStringToString(const std::wstring& src)
{
 std::string str(src.length(), ' ');
 std::copy(src.begin(), src.end(), str.begin());
 return str; 
}

void main()
{
 string s1 = "Hello World!";
 wstring s2 = StringToWString(s1);
 s1 = WStringToString(s2);
} 
person wengseng    schedule 28.09.2010
comment
Я не думаю, что WStringToString правильный. Вы просто конвертируете wchar_t в char. Это не меняет кодировку и должно нарушать кодовые точки, не представленные ascii. - person catphive; 29.09.2010