Поставлен в тупик с Unicode, Boost, C++, codecvts
В C++ я хочу использовать Unicode, чтобы делать вещи. Так что, упав в кроличью нору Unicode, мне удалось оказаться в железнодорожной катастрофе путаницы, головных болей и мест.
Но в Boost у меня возникла неудачная проблема: пытаться использовать пути к файлам Unicode и пытаться использовать библиотеку опций программы Boost с вводом Unicode. Я прочитал все, что я мог найти на предметах локалей, codecvts, кодировок Unicode и Boost.
Моя текущая попытка заставить вещи работать - иметь codecvt, который принимает строку UTF-8 и преобразует ее в кодировку платформы (UTF-8 в POSIX, UTF-16 в Windows), я пытался избежать wchar_t
,
Самое близкое, что я на самом деле получил, - это попытаться сделать это с Boost.Locale, чтобы преобразовать строку UTF-8 в строку UTF-32 на выходе.
#include <string>
#include <boost/locale.hpp>
#include <locale>
int main(void)
{
std::string data("Testing, 㤹");
std::locale fromLoc = boost::locale::generator().generate("en_US.UTF-8");
std::locale toLoc = boost::locale::generator().generate("en_US.UTF-32");
typedef std::codecvt<wchar_t, char, mbstate_t> cvtType;
cvtType const* toCvt = &std::use_facet<cvtType>(toLoc);
std::locale convLoc = std::locale(fromLoc, toCvt);
std::cout.imbue(convLoc);
std::cout << data << std::endl;
// Output is unconverted -- what?
return 0;
}
Я думаю, что у меня был какой-то другой вид конверсии, работающий с использованием широких символов, но я действительно не знаю, что я вообще делаю. Я не знаю, какой инструмент лучше всего подходит для этой работы. Помогите?
3 ответа
Хорошо, после долгих нескольких месяцев я понял это, и я хотел бы помочь людям в будущем.
Прежде всего, Codecvt был неправильным способом сделать это. Boost.Locale предоставляет простой способ преобразования между наборами символов в его пространстве имен boost::locale::conv. Вот один пример (есть другие, не основанные на локалях).
#include <boost/locale.hpp>
namespace loc = boost::locale;
int main(void)
{
loc::generator gen;
std::locale blah = gen.generate("en_US.utf-32");
std::string UTF8String = "Tésting!";
// from_utf will also work with wide strings as it uses the character size
// to detect the encoding.
std::string converted = loc::conv::from_utf(UTF8String, blah);
// Outputs a UTF-32 string.
std::cout << converted << std::endl;
return 0;
}
Как вы можете видеть, если вы замените "en_US.utf-32" на "", он будет выводиться в локали пользователя.
Я до сих пор не знаю, как заставить std::cout делать это все время, но функция translate() Boost.Locale выводит в локали пользователя.
Что касается файловой системы, использующей кроссплатформенность строк UTF-8, кажется, что это возможно, вот ссылка на то, как это сделать.
Классы замены iostream файловой системы Boost отлично работают с UTF-16 при использовании с Visual C++.
Однако они не работают (в смысле поддержки произвольных имен файлов) при использовании с g++ в Windows - по крайней мере, в версии Boost 1.47. Есть комментарий к коду, объясняющий это; по сути, стандартная библиотека Visual C++ предоставляет нестандартные wchar_t
основанные конструкторы, которые используют классы файловой системы Boost, но g++ не поддерживает эти расширения.
Обходной путь - использовать 8.3 коротких имен файлов, но это решение немного хрупко, поскольку в старых версиях Windows пользователь может отключить автоматическую генерацию коротких имен файлов.
Пример кода для использования файловой системы Boost в Windows:
#include "CmdLineArgs.h" // CmdLineArgs
#include "throwx.h" // throwX, hopefully
#include "string_conversions.h" // ansiOrFillerFrom( wstring )
#include <boost/filesystem/fstream.hpp> // boost::filesystem::ifstream
#include <iostream> // std::cout, std::cerr, std::endl
#include <stdexcept> // std::runtime_error, std::exception
#include <string> // std::string
#include <stdlib.h> // EXIT_SUCCESS, EXIT_FAILURE
using namespace std;
namespace bfs = boost::filesystem;
inline string ansi( wstring const& ws ) { return ansiWithFillersFrom( ws ); }
int main()
{
try
{
CmdLineArgs const args;
wstring const programPath = args.at( 0 );
hopefully( args.nArgs() == 2 )
|| throwX( "Usage: " + ansi( programPath ) + " FILENAME" );
wstring const filePath = args.at( 1 );
bfs::ifstream stream( filePath ); // Nice Boost ifstream subclass.
hopefully( !stream.fail() )
|| throwX( "Failed to open file '" + ansi( filePath ) + "'" );
string line;
while( getline( stream, line ) )
{
cout << line << endl;
}
hopefully( stream.eof() )
|| throwX( "Failed to list contents of file '" + ansi( filePath ) + "'" );
return EXIT_SUCCESS;
}
catch( exception const& x )
{
cerr << "!" << x.what() << endl;
}
return EXIT_FAILURE;
}
std::cout.imbue(convLoc);
std::cout << data << std::endl;
Это не конвертация, так как он использует codecvt<char, char, mbstate_t>
который не работает. Единственными стандартными потоками, которые используют codecvt, являются файловые потоки. std::cout не требуется для выполнения какого-либо преобразования вообще.
Чтобы заставить Boost.Filesystem интерпретировать узкие строки как UTF-8 в Windows, используйтеboost::filesystem::imbue
с локалью с кодовым аспектом UTF-8 ↔ UTF-16. Boost.Locale имеет реализацию последнего.