Хотя мой опыт работы с C ++ довольно ограничен, я пытаюсь помочь программисту C ++ работать с его библиотекой на Mac. На данный момент проблема, похоже, связана только с локалью / кодировкой.
Пытаясь создать минимальный рабочий пример, я протестировал следующий код, который читает строку символов UTF-8 в широкую строку (wstring), а затем проходит через строку и печатает каждый символ.
Хотя он отлично работает на Linux-боксе, когда все символы печатаются в отдельной строке, при использовании Mac-бокса я получаю каждый байт печатается на строку (а не на каждый символ).
Код является:
#include <sstream>
#include <iostream>
#include <string>
#include <boost/locale.hpp>
using namespace std;
int main() {
std::ios_base::sync_with_stdio(false);
boost::locale::generator gen;
locale mylocale = gen("pt_PT.UTF-8");
locale::global(mylocale);
wstring userInput;
getline(wcin, userInput);
wcerr << "Size of string is " << userInput.length() << endl;
for (int i = 0; i < userInput.length(); ++i) {
wcerr << userInput.at(i) << endl;
}
return 0;
}
и моя тестовая строка — глупое португальское предложение:
O coração é um órgão frágil.
Я пытаюсь использовать Boost_locale, потому что кто-то сказал мне, что это способ заставить Юникод работать корректно на Mac, но я был бы рад найти решение, использующее только стандартные библиотеки C ++.
РЕДАКТИРОВАТЬ:
Следующий код работает на Mac. Он не компилируется на моем Linux-компьютере из-за включения codecvt, но я могу справиться с этим с помощью некоторых инструкций CPP.
#include <sstream>
#include <iostream>
#include <fstream>
#include <codecvt>
#include <locale>
#include <string>
using namespace std;
int main() {
// setting std::local::global seems not to work (??)
wcin.imbue(std::locale(locale(""), new std::codecvt_utf8<wchar_t>));
wcerr.imbue(std::locale(locale(""), new std::codecvt_utf8<wchar_t>));
wstring userInput;
getline(wcin, userInput);
wcerr << "Size of string is " << userInput.length() << endl;
for (int i = 0; i < userInput.length(); ++i) {
wcerr << userInput.at(i) << endl;
}
return 0;
}
Такое поведение вызвано тем, что в кодировке UTF-8 символ, также известный как кодовая точка представлен одним или несколькими кодовые единицы.
По сути то:
for (int i = 0; i < userInput.length(); ++i)
проходит через кодовые единицы. Вы можете проверить это поведение по тому факту, что userInput.length()
это число больше, чем количество символов в вашей строке.
При выполнении:
wcerr << userInput.at(i) << endl;
Вы добавляете endl
после каждого кодовая единица и, таким образом, отделяя кодовые единицы которые принадлежат одному и тому же кодовая точка который производит недопустимые символы.
Если вы вместо этого просто выводите:
wcerr << userInput << endl;
Вы получите вашу строку в целости и сохранности.
Если вы хотите вывести каждый символ отдельно, вам нужно будет учитывать несколько кодовые единицы которые принадлежат одному и тому же кодовая точка и выводить их отдельно.
ОБНОВИТЬ:
wcin
не делает преобразование в кодовые точки по умолчанию. Вам необходимо явно указать кодировку ввода и преобразовать ее. По сути, это то, что делает следующий код. Единственное существенное отличие вашего примера в том, что я использовал C ++ 11 стандартная библиотека вместо Увеличение.
#include <codecvt>
#include <iostream>
int main() {
std::locale::global( std::locale( std::locale(""), new std::codecvt_utf8<wchar_t> ) );
std::wcin.imbue( std::locale() );
std::wcout.imbue( std::locale() );
std::wcerr.imbue( std::locale() );
std::wstring user_input;
std::wcin >> user_input;
for( int i = 0; i < user_input.length(); ++i ) {
std::wcout << user_input[i] << std::endl;
}
// Converting characters to uppercase
const std::ctype<wchar_t>& f = std::use_facet<std::ctype<wchar_t>>( std::locale() );
for( int i = 0; i < user_input.length(); ++i ) {
std::wcout << f.toupper(user_input[i]) << std::endl; // f.tolower() for lowercase
}
return 0;
}
Постскриптум Чтобы скомпилировать что вам нужно будет передать C ++ 11 стандартный флаг.
g++ -std=c++11 main.cpp
Других решений пока нет …