2017-01-23 7 views
4

Там, кажется, проблема, когда я пишу слово в иностранных символах (французских ...)символов поддерживаются в C++

Например, если я прошу ввод для станд :: строки или полукокс [ ] следующим образом:

std::string s; 
std::cin>>s; //if we input the string "café" 
std::cout<<s<<std::endl; //outputs "café" 

Все в порядке.

Хотя, если строка жестко

std::string s="café"; 
std::cout<<s<<std::endl; //outputs "cafÚ" 

Что происходит? Какие символы поддерживаются C++ и как я могу заставить его работать правильно? Связано ли это с моей операционной системой (Windows 10)? Моя IDE (VS 15)? Или с C++?

+9

В какой кодировке вы сохраняете исходный файл? –

+3

Вы можете использовать строковые литералы utf8 в C++ 11 и более поздних версиях: http://en.cppreference.com/w/cpp/language/string_literal – BitTickler

+0

С u8 он выводит «caf├®» ... И я понятия не имею о кодировании ... –

ответ

1

Какие символы поддерживаются C++

standarad C++ не определяет, какие символы поддерживаются. Это конкретная реализация.

Есть ли у него что-то делать с ...

... C++?

No.

... Мой IDE?

Нет, хотя IDE может иметь возможность редактировать исходный файл в частности кодировке.

... моя операционная система?

Это может иметь влияние.

На это влияют несколько факторов.

  • Что такое кодировка исходного файла.
  • Какова кодировка, используемая компилятором для интерпретации исходного файла.
    • Это то же самое, что и кодировка файла или другая (она должна быть такой же или может работать некорректно).
    • Нативная кодировка вашей операционной системы, вероятно, влияет на кодировку, которую кодирует ваш компилятор по умолчанию.
  • Какая кодировка выполняет терминал, поддерживающий поддержку программы.
    • Это то же самое, что и кодирование файла или другое (оно должно быть таким же или может работать неправильно без преобразования).
  • Используется кодировка символов широкоформатная. В широком смысле я имею в виду, что ширина блока кода больше CHAR_BIT.Широкий источник/компилятор вызовет преобразование в другую узкую кодировку, поскольку вы используете узкий строковый литерал и узкий оператор потока. В этом случае вам нужно будет определить как узкую, так и собственную широкую кодировку символов, ожидаемую компилятором. Компилятор преобразует входную строку в узкую кодировку. Если узкая кодировка не имеет представления для символа во входной кодировке, она может работать неправильно.

Пример:

Исходный файл кодируется в UTF-8. Компилятор ожидает UTF-8. Терминал ожидает UTF-8. В этом случае то, что вы видите, это то, что вы получаете.

+1

Я вижу. Итак, как я могу проверить все это и как заставить его работать? –

+1

Еще одна вещь: char a = 'é'; std :: cout << int (a); // выходы -23 и char a; std :: cin >> a; // ввод 'é' std :: cout << int (a); // выходы -126 –

+0

@TomDorone Я могу вам помочь, но сначала ответьте на эти вопросы: Какова кодировка файла, которую ожидает ваш компилятор? Если это широкий, то какова местная узкая кодировка? Какова кодировка, которую ожидает ваш терминал? – user2079303

3

В двух словах, если вы хотите передать/получить текст Unicode в/из консоли в Windows 10 (фактически, любую версию Windows), вам нужно использовать широкие строки IE, std :: wstring. Сама Windows не поддерживает кодировку UTF-8. Это фундаментальное ограничение ОС.

Весь API Win32, на котором основаны такие функции, как доступ к консоли и файловой системе, работает только с символами Unicode под кодировкой UTF-16, а время выполнения C/C++, предоставляемое в Visual Studio, не предлагает никаких слоя перевода, чтобы сделать этот API UTF-8 совместимым. Это не означает, что вы не можете использовать внутреннюю кодировку UTF-8, это просто означает, что, когда вы нажмете на Win32 API или функцию времени исполнения C/C++, которая его использует, вам нужно будет конвертировать между UTF-8 и UTF -16. Это отстой, но это именно то, где мы сейчас.

Некоторые люди могут направить вас к серии трюков, которые пропорциональны работе консоли с UTF-8. Не ходите по этому маршруту, у вас будет много проблем. Для доступа к консоли Unicode поддерживаются только широкосимвольные строки.

Edit: Поскольку UTF-8/UTF-16 строка преобразования нетривиален, и там тоже не большая помощь предоставляется для этого в C++, вот некоторые функции преобразования I, полученные ранее:

/////////////////////////////////////////////////////////////////////////////////////////////////// 
std::wstring UTF8ToUTF16(const std::string& stringUTF8) 
{ 
    // Convert the encoding of the supplied string 
    std::wstring stringUTF16; 
    size_t sourceStringPos = 0; 
    size_t sourceStringSize = stringUTF8.size(); 
    stringUTF16.reserve(sourceStringSize); 
    while (sourceStringPos < sourceStringSize) 
    { 
     // Determine the number of code units required for the next character 
     static const unsigned int codeUnitCountLookup[] = { 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 4 }; 
     unsigned int codeUnitCount = codeUnitCountLookup[(unsigned char)stringUTF8[sourceStringPos] >> 4]; 

     // Ensure that the requested number of code units are left in the source string 
     if ((sourceStringPos + codeUnitCount) > sourceStringSize) 
     { 
      break; 
     } 

     // Convert the encoding of this character 
     switch (codeUnitCount) 
     { 
     case 1: 
     { 
      stringUTF16.push_back((wchar_t)stringUTF8[sourceStringPos]); 
      break; 
     } 
     case 2: 
     { 
      unsigned int unicodeCodePoint = (((unsigned int)stringUTF8[sourceStringPos] & 0x1F) << 6) | 
              ((unsigned int)stringUTF8[sourceStringPos + 1] & 0x3F); 
      stringUTF16.push_back((wchar_t)unicodeCodePoint); 
      break; 
     } 
     case 3: 
     { 
      unsigned int unicodeCodePoint = (((unsigned int)stringUTF8[sourceStringPos] & 0x0F) << 12) | 
              (((unsigned int)stringUTF8[sourceStringPos + 1] & 0x3F) << 6) | 
              ((unsigned int)stringUTF8[sourceStringPos + 2] & 0x3F); 
      stringUTF16.push_back((wchar_t)unicodeCodePoint); 
      break; 
     } 
     case 4: 
     { 
      unsigned int unicodeCodePoint = (((unsigned int)stringUTF8[sourceStringPos] & 0x07) << 18) | 
              (((unsigned int)stringUTF8[sourceStringPos + 1] & 0x3F) << 12) | 
              (((unsigned int)stringUTF8[sourceStringPos + 2] & 0x3F) << 6) | 
              ((unsigned int)stringUTF8[sourceStringPos + 3] & 0x3F); 
      wchar_t convertedCodeUnit1 = 0xD800 | (((unicodeCodePoint - 0x10000) >> 10) & 0x03FF); 
      wchar_t convertedCodeUnit2 = 0xDC00 | ((unicodeCodePoint - 0x10000) & 0x03FF); 
      stringUTF16.push_back(convertedCodeUnit1); 
      stringUTF16.push_back(convertedCodeUnit2); 
      break; 
     } 
     } 

     // Advance past the converted code units 
     sourceStringPos += codeUnitCount; 
    } 

    // Return the converted string to the caller 
    return stringUTF16; 
} 

/////////////////////////////////////////////////////////////////////////////////////////////////// 
std::string UTF16ToUTF8(const std::wstring& stringUTF16) 
{ 
    // Convert the encoding of the supplied string 
    std::string stringUTF8; 
    size_t sourceStringPos = 0; 
    size_t sourceStringSize = stringUTF16.size(); 
    stringUTF8.reserve(sourceStringSize * 2); 
    while (sourceStringPos < sourceStringSize) 
    { 
     // Check if a surrogate pair is used for this character 
     bool usesSurrogatePair = (((unsigned int)stringUTF16[sourceStringPos] & 0xF800) == 0xD800); 

     // Ensure that the requested number of code units are left in the source string 
     if (usesSurrogatePair && ((sourceStringPos + 2) > sourceStringSize)) 
     { 
      break; 
     } 

     // Decode the character from UTF-16 encoding 
     unsigned int unicodeCodePoint; 
     if (usesSurrogatePair) 
     { 
      unicodeCodePoint = 0x10000 + ((((unsigned int)stringUTF16[sourceStringPos] & 0x03FF) << 10) | ((unsigned int)stringUTF16[sourceStringPos + 1] & 0x03FF)); 
     } 
     else 
     { 
      unicodeCodePoint = (unsigned int)stringUTF16[sourceStringPos]; 
     } 

     // Encode the character into UTF-8 encoding 
     if (unicodeCodePoint <= 0x7F) 
     { 
      stringUTF8.push_back((char)unicodeCodePoint); 
     } 
     else if (unicodeCodePoint <= 0x07FF) 
     { 
      char convertedCodeUnit1 = (char)(0xC0 | (unicodeCodePoint >> 6)); 
      char convertedCodeUnit2 = (char)(0x80 | (unicodeCodePoint & 0x3F)); 
      stringUTF8.push_back(convertedCodeUnit1); 
      stringUTF8.push_back(convertedCodeUnit2); 
     } 
     else if (unicodeCodePoint <= 0xFFFF) 
     { 
      char convertedCodeUnit1 = (char)(0xE0 | (unicodeCodePoint >> 12)); 
      char convertedCodeUnit2 = (char)(0x80 | ((unicodeCodePoint >> 6) & 0x3F)); 
      char convertedCodeUnit3 = (char)(0x80 | (unicodeCodePoint & 0x3F)); 
      stringUTF8.push_back(convertedCodeUnit1); 
      stringUTF8.push_back(convertedCodeUnit2); 
      stringUTF8.push_back(convertedCodeUnit3); 
     } 
     else 
     { 
      char convertedCodeUnit1 = (char)(0xF0 | (unicodeCodePoint >> 18)); 
      char convertedCodeUnit2 = (char)(0x80 | ((unicodeCodePoint >> 12) & 0x3F)); 
      char convertedCodeUnit3 = (char)(0x80 | ((unicodeCodePoint >> 6) & 0x3F)); 
      char convertedCodeUnit4 = (char)(0x80 | (unicodeCodePoint & 0x3F)); 
      stringUTF8.push_back(convertedCodeUnit1); 
      stringUTF8.push_back(convertedCodeUnit2); 
      stringUTF8.push_back(convertedCodeUnit3); 
      stringUTF8.push_back(convertedCodeUnit4); 
     } 

     // Advance past the converted code units 
     sourceStringPos += (usesSurrogatePair) ? 2 : 1; 
    } 

    // Return the converted string to the caller 
    return stringUTF8; 
} 

Я был ответственным за незавидную задачу преобразования 6-миллионного унаследованного приложения Windows для поддержки Unicode, когда он был написан только для поддержки ASCII (на самом деле его предварительный формат Unicode), где мы использовали std :: string и char [] для хранения строк. Поскольку изменение всех внутренних буферов хранения строк было просто невозможно, нам нужно было внедрить UTF-8 внутренне и преобразовать между UTF-8 и UTF-16 при попадании в Win32 API. Это функции преобразования, которые мы использовали.

Я настоятельно рекомендую придерживаться того, что поддерживается для новой разработки Windows, что означает широкие строки. Тем не менее, нет причин, по которым вы не можете основывать ядро ​​своей программы на строках UTF-8, но при взаимодействии с Windows и различных аспектах времени работы C/C++ это будет более сложно.

Редактировать 2: Я только что перечитал исходный вопрос, и я вижу, что я не очень хорошо ответил на него. Позвольте мне дать дополнительную информацию, которая специально ответит на ваш вопрос.

Что происходит? При разработке с C++ в Windows, когда вы используете std :: string с std :: cin/std :: cout, консоль ввода-вывода выполняется с использованием кодирования MBCS. Это устаревший режим, при котором символы кодируются с использованием выбранного на данный момент code page. Значения, закодированные под этими кодовыми страницами, не являются unicode и не могут использоваться совместно с другими системами, для которых выбрана другая кодовая страница, или даже с той же системой, если кодовая страница изменена. Он отлично работает в вашем тесте, потому что вы записываете ввод под текущей кодовой страницей и отображаете его на той же кодовой странице.Если вы попытаетесь захватить этот ввод и сохранить его в файл, проверка покажет, что он не является юникодом. Загрузите его с другой кодовой страницей, выбранной в нашей ОС, и текст будет поврежден. Вы можете интерпретировать текст только в том случае, если вы знаете, на какой кодовой странице он был закодирован. Поскольку эти страницы устаревшего кода являются региональными, и ни один из них не может представлять все текстовые символы, это делает невозможным совместное использование общего текста на разных компьютерах и компьютерах. MBCS предваряет развитие юникода, и именно из-за таких проблем, которые был изобретен unicode. Unicode - это, в основном, «одна кодовая страница, чтобы управлять всеми ими». Возможно, вам интересно, почему UTF-8 не является выбираемой «старой» кодовой страницей в Windows. Многим из нас интересно то же самое. Достаточно сказать, что это не так. Таким образом, вы не должны полагаться на кодировку MBCS, потому что вы не можете получить поддержку Unicode при ее использовании. Единственный вариант поддержки Unicode в Windows - использование std :: wstring и вызов UTF-16 Win32 API.

Что касается вашего примера о жестко закодированной струне, то прежде всего понять, что кодирование текста, отличного от ASCII, в исходный файл помещает вас в область поведения, специфичного для компилятора. В Visual Studio вы можете указать кодировку исходного файла (в разделе «Файл-> Расширенные параметры сохранения»). В вашем случае текст отличается от того, что вы ожидаете, потому что он кодируется (скорее всего) в UTF-8, но, как уже упоминалось, вывод консоли выполняется с использованием кодирования MBCS на выбранной вами кодовой странице, которая не UTF-8. Исторически вам было бы рекомендовано избегать любых символов, отличных от ASCII, в исходных файлах и избегать использования с помощью обозначения \ x. Сегодня существуют C++ 11 string prefixes and suffixes, которые гарантируют различные формы кодирования. Вы можете попробовать использовать их, если вам нужна эта способность. У меня нет практического опыта их использования, поэтому я не могу сообщить, есть ли какие-либо проблемы с этим подходом.

+3

Зачем писать код преобразования UTF8 ↔ UTF16 вручную? У вас возникли проблемы с функциями API «WideCharToMultiByte» и «MultiByteToWideChar»? –

+0

Да, но я больше не помню подробностей. Я уверен, что функции работают, поэтому, вероятно, это просто вопрос производительности, они уверены, что они очень медленны по сравнению с ними. –

+5

"* в C++ также не так много справки *« Существует ['std :: codecvt_utf8_utf16'] (http://en.cppreference.com/w/cpp/locale/codecvt_utf8_utf16) и [' std: : wstring_convert'] (http://en.cppreference.com/w/cpp/locale/wstring_convert), которые сделаны специально для этого. И IME их производительность не очень медленная. ; -] – ildjarn

2

Проблема возникает из-за самой Windows. Он использует одну кодировку символов (UTF-16) для большинства внутренних операций, другую (Windows-1252) для кодировки файлов по умолчанию и еще одну (Code Page 850 в вашем случае) для консольных операций ввода-вывода. Ваш исходный файл закодирован в Windows-1252, где é соответствует одному байту '\xe9'. Когда вы показываете этот же код в кодовой странице 850, он становится Ú. Использование u8"é" создает два байта последовательность "\xc3\xa9", которая печатает на консоли как ├®.

Возможно, самое простое решение - не помещать в свой код не-ASCII-литералы и использовать шестнадцатеричный код для требуемого персонажа. Однако это не будет красивым или портативным решением.

std::string s="caf\x82"; 

Лучшим решением было бы использовать u16 строки и кодировать их с помощью WideCharToMultiByte.

0

Хитрости здесь setlocale:

#include <clocale> 
#include <string> 
#include <iostream> 

int main() { 
    std::setlocale(LC_ALL, ""); 
    std::string const s("café"); 
    std::cout << s << '\n'; 
} 

Выход для меня с помощью командной строки для Windows 10 является правильным, даже без изменения терминала кодового.

 Смежные вопросы

  • Нет связанных вопросов^_^