macos — чтение данных UTF-8 с C ++ в Mac не работает

Хотя мой опыт работы с 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;
}

1

Решение

Такое поведение вызвано тем, что в кодировке 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
1

Другие решения

Других решений пока нет …