Как конвертировать UTF-8 в ASCII в C ++? - PullRequest
8 голосов
/ 05 июня 2010

Я получаю ответ от сервера в utf-8, но не могу прочитать это. Как преобразовать UTF-8 в ASCII в C ++?

Ответы [ 9 ]

23 голосов
/ 05 июня 2010

Первое замечание, что ASCII - это 7-битный формат. Существуют 8-битные кодировки, если вам нужно одно из них (например, ISO 8859-1), вам нужно быть более конкретным.

Чтобы преобразовать строку ASCII в UTF-8, ничего не делайте: они одинаковы. Поэтому, если ваша строка UTF-8 состоит из только символов ASCII, то это уже строка ASCII, и преобразование не требуется.

Если строка UTF-8 содержит не-ASCII-символы (что-либо с акцентами или нелатинскими символами), преобразовать ее в ASCII невозможно. (Возможно, вы сможете преобразовать его в одну из кодировок ISO.)

Есть способы убрать акценты с латинских символов, чтобы получить хоть какое-то сходство с ASCII. В качестве альтернативы, если вы просто хотите удалить не-ASCII-символы, просто удалите все байты со значениями> = 128 из строки utf-8.

10 голосов
/ 05 июня 2010

Этот пример работает под Windows (вы не упомянули целевую операционную систему):

    // The sample buffer contains "©ha®a©te®s" in UTF-8
    unsigned char buffer[15] = { 0xc2, 0xa9, 0x68, 0x61, 0xc2, 0xae, 0x61, 0xc2, 0xa9, 0x74, 0x65, 0xc2, 0xae, 0x73, 0x00 };
    // utf8 is the pointer to your UTF-8 string
    char* utf8 = (char*)buffer;
    // convert multibyte UTF-8 to wide string UTF-16
    int length = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)utf8, -1, NULL, 0);
    if (length > 0)
    {
        wchar_t* wide = new wchar_t[length];
        MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)utf8, -1, wide, length);

        // convert it to ANSI, use setlocale() to set your locale, if not set
        size_t convertedChars = 0;
        char* ansi = new char[length];
        wcstombs_s(&convertedChars, ansi, length, wide, _TRUNCATE);
    }

Не забывайте delete[] wide; и / или ansi, когда они больше не нужны. Поскольку это юникод, я бы рекомендовал придерживаться wchar_t* вместо char*, если только вы не уверены, что входной буфер содержит символы, принадлежащие к одному и тому же подмножеству ANSI.

5 голосов
/ 05 июня 2010

Если строка содержит символы, которые не существуют в ASCII, то вы ничего не можете сделать, потому что, ну, эти символы не существуют в ASCII.

Если строка содержит только символов, которые do существуют в ASCII, то вам ничего не нужно сделать, потому что строка уже в кодировке ASCII: UTF-8 был специально разработан для обратной совместимости с ASCII таким образом, чтобы любой символ в ASCII имел ту же самую кодировку в UTF-8, что и в ASCII, и что любой символ, который не в ASCII, может никогда иметь кодировку, которая является действительной ASCII, т.е. будет всегда иметь кодировку, которая недопустима в ASCII (в частности, любая не - Символ ASCII будет закодирован как последовательность из 2 - 4 октетов, каждый из которых имеет установленный старший бит, т. Е. Имеет целочисленное значение> 127).

Вместо того, чтобы просто пытаться преобразовать строку, вы можете попытаться транслитерировать строку. Большинство языков на этой планете имеют некоторую форму схемы транслитерации ASCII, которая, по крайней мере, делает текст несколько понятным. Например, меня зовут «Jörg», а его транслитерацией ASCII будет «Joerg». Имя создателя языка программирования Ruby - "ま つ も と ゆ き ひ ろ", а его транслитерацией ASCII будет "Мацумото Юкихиро". Тем не менее, обратите внимание, что вы потеряете информацию. Например, немецкая sz-ligature транслитерируется на «ss», поэтому слово «Maße» (измерения) транслитерируется на «Masse». Тем не менее, «масса» (масса, в смысле физика, а не христианина) составляет и слово. В качестве другого примера, у турецкого языка есть 4 "i" (маленький и большой, с точкой и без точки), а в ASCII только 2 (маленький с точкой и большой буквы без точки), поэтому вы либо потеряете информацию о точке, либо нет была заглавная буква.

Таким образом, способ only , который не потеряет информацию (другими словами: поврежденные данные), заключается в том, чтобы каким-то образом кодировать не-ASCII-символы в последовательности символов ASCII. Существует много популярных схем кодирования: ссылки на объекты SGML, MIME, escape-последовательности Unicode, Τ & Epsilon; & Chi; или LaΤ & Epsilon; & Chi ;. Таким образом, вы будете кодировать данные, когда они поступают в вашу систему, и декодировать их, когда они покидают систему.

Конечно, самым простым способом было бы просто исправить вашу систему.

4 голосов
/ 05 июня 2010

UTF-8 - это кодировка, которая может отображать каждый символ Юникода. ASCII поддерживает только очень маленькое подмножество юникода.

Для подмножества Unicode, которое является ASCII, отображение из UTF-8 в ASCII является прямым однозначным байтовым отображением, поэтому, если сервер отправляет вам документ, содержащий только символы ASCII в кодировке UTF-8, Вы можете непосредственно прочитать это как ASCII.

Если ответ содержит не-ASCII символы, то, что бы вы ни делали, вы не сможете выразить их в ASCII. Чтобы отфильтровать их из потока UTF-8, вы можете просто отфильтровать любой байт> = 128 (шестнадцатеричный 0x80).

1 голос
/ 05 июня 2010

Проверьте это utf-8 String Library , забудьте о преобразовании его в ASCII.

0 голосов
/ 26 декабря 2013

Обратите внимание, что существует два типа UTF8: UTF8_with_BOM и UTF8_without_BOM . И вам нужно обрабатывать их по-разному при конвертации в ANSI. Следующие функции будут работать.

  • UTF8_with_BOM до ANSI

    void change_encoding_from_UTF8_with_BOM_to_ANSI(const char* filename)
    {
        ifstream infile;
        string strLine="";
        string strResult="";
        infile.open(filename);
        if (infile)
        {
            // the first 3 bytes (ef bb bf) is UTF-8 header flags
            // all the others are single byte ASCII code.
            // should delete these 3 when output
            getline(infile, strLine);
            strResult += strLine.substr(3)+"\n";
    
            while(!infile.eof())
            {
                getline(infile, strLine);
                strResult += strLine+"\n";
            }
        }
        infile.close();
    
        char* changeTemp=new char[strResult.length()];
        strcpy(changeTemp, strResult.c_str());
        char* changeResult = change_encoding_from_UTF8_to_ANSI(changeTemp);
        strResult=changeResult;
    
        ofstream outfile;
        outfile.open(filename);
        outfile.write(strResult.c_str(),strResult.length());
        outfile.flush();
        outfile.close();
    }
    
    // change a char's encoding from UTF8 to ANSI
    char* change_encoding_from_UTF8_to_ANSI(char* szU8)
    { 
        int wcsLen = ::MultiByteToWideChar(CP_UTF8, NULL, szU8, strlen(szU8), NULL, 0);
        wchar_t* wszString = new wchar_t[wcsLen + 1];
        ::MultiByteToWideChar(CP_UTF8, NULL, szU8, strlen(szU8), wszString, wcsLen);
        wszString[wcsLen] = '\0';
    
        int ansiLen = ::WideCharToMultiByte(CP_ACP, NULL, wszString, wcslen(wszString), NULL, 0, NULL, NULL);
        char* szAnsi = new char[ansiLen + 1];
        ::WideCharToMultiByte(CP_ACP, NULL, wszString, wcslen(wszString), szAnsi, ansiLen, NULL, NULL);
        szAnsi[ansiLen] = '\0';
    
        return szAnsi;
    }
    
  • UTF8_without_BOM до ANSI

    void change_encoding_from_UTF8_without_BOM_to_ANSI(const char* filename)
    {
        ifstream infile;
        string strLine="";
        string strResult="";
        infile.open(filename);
        if (infile)
        {
            while(!infile.eof())
            {
                getline(infile, strLine);
                strResult += strLine+"\n";
            }
        }
        infile.close();
    
        char* changeTemp=new char[strResult.length()];
        strcpy(changeTemp, strResult.c_str());
        char* changeResult = change_encoding_from_UTF8_to_ANSI(changeTemp);
        strResult=changeResult;
    
        ofstream outfile;
        outfile.open(filename);
        outfile.write(strResult.c_str(),strResult.length());
        outfile.flush();
        outfile.close();
    }
    
0 голосов
/ 28 июля 2013

Что касается фразы

«Если строка содержит символы, которые не существуют в ASCII, то вы ничего не можете сделать, потому что, ну, эти символы не существуют в ASCII»

это неправильно.

UTF-8 - это набор многобайтовых кодов, который может содержать более 2 наборов символов (языков). Практически у вас есть один язык (как обычно английский) или 2 языка, один из которых английский.

  • Первый случай - простой символ ASCII (любая кодировка).
  • Второй описывает кодировку ASCII, соответствующую кодировке. Если это не китайский или арабский.

В вышеприведенных условиях вы можете конвертировать UTF-8 в символы ASCII. Соответствующего функционала нет в C ++. Так что вы можете сделать это вручную. Легко обнаружить два байтовых символа из 1 байта. Старший бит первого байта устанавливается на два байта и устанавливается в противном случае.

0 голосов
/ 05 июня 2010

ASCII - это кодовая страница, представляющая 128 символов и управляющие коды, где utf8 может представлять любой символ в стандарте Unicode, который намного-намного больше возможностей ASCII. Поэтому ответ на ваш вопрос: не представляется возможным Если у вас нет дополнительных спецификаций для источника данных.

0 голосов
/ 05 июня 2010

UTF-8 обратно совместим с ASCII, что означает, что все символы ASCII кодируются как одно неизмененные байтовые значения в UTF-8. Если текст должен быть в формате ASCII, но вы не можете его прочитать, тогда должна быть другая проблема.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...