В качестве предисловия я не сомневаюсь, что мои знания Unicode и кодировки символов неполны. Извините за любые искажения.
Недавно я разрабатывал приложение, которое будет обрабатывать входящие данные в различных кодировках. Это, например, UTF-8, ASCII, SHIFT-JIS и т. Д.
Теперь я понимаю, что для данного кодирования существует «отображение» между ним и данным значением Unicode. Возьмем, к примеру, символ «あ».
Значение Юникода будет U + 3042, значение UTF-8 будет \ xe3 \ x81 \ x82, и, наконец, значение SHIFT-JIS будет \ x82 \ xa0.
Все они могут быть непосредственно представлены в виде потока байтов с прямым отображением, однако преобразование между ними представляется сложной задачей.
С таким языком, как Python, ниже может бытьИспользуется:
utf8_val = "あ"
unicode_val = utf8_val.decode("utf-8")
sjis_val = unicode_val.encode("shift-jis", "ignore")
Я понимаю, что ICU или Boost.Locale могут достичь аналогичных результатов, используя функции to_utf
и from_utf
, однако, почему не существует какого-либо базового способа добиться этого с помощью стандартногобиблиотека?
Похоже, что преобразования преобразования существуют в Microsoft STL (https://github.com/microsoft/STL/tree/master/stl/inc/cvt), однако без реальной документации о них неясно, как их использовать.
НаконецПохоже, что этого можно достичь с помощью библиотеки <locale>
, однако в документации нет четких ответов о том, как это сделать. Мое лучшее предположение - сделать с codecvt :: in и codecvt. :: out однако даже здесь неясно, как правильно подходить.
Я просто что-то пропускаю? Или преобразование действительно так сложно?
edit: я попытался выполнить базовое преобразование char-> unicode, используя документацию из MS (здесь) :
std::string conv(std::string str, std::string enc)
{
std::locale loc(enc.c_str());
std::string dest;
dest.resize(str.length() * 2);
char* pnewNext;
const char* porigNext;
std::mbstate_t mbstate;
auto res = std::use_facet<std::codecvt<char, char, std::mbstate_t>>
(loc).out(mbstate,
&str[0], &str[str.size()-1], porigNext,
&dest[0], &dest[dest.length()-1], pnewNext);
std::cout << "errcode : " << res << std::endl;
for(const auto& c : str)
{
std::cout << std::hex << std::setw(2) << std::setfill('0') << (uint)(unsigned char)c << std::endl;
}
for(const auto& c : dest)
{
std::cout << std::hex << std::setw(2) << std::setfill('0') << (uint)(unsigned char)c << std::endl;
}
return dest;
}
К сожалению, этот подход все еще кажется неправильным:
errcode : 3 // this is std::codecvt_base::noconv
33 //these are the input bytes
44
00 //these are the output bytes
00
00
00
Для тех, кто может столкнуться с этим, я написал (плохой) алгоритм преобразования UTF-8 в Unicode. Это определенно можно улучшить (как по производительности, так и по размеру кода). Несмотря на это, он успешно конвертируется (без каких-либо реальных проверок - будьте предупреждены).
std::size_t ucsz(char c)
{
std::size_t count = 0;
for (std::size_t i = 7; i > 0; --i)
{
if (((c >> i) & 1) == 0)
{
return std::max(static_cast<int>(count), 1);
}
++count;
}
return count;
}
std::string utf8_to_unicode(std::string utf8)
{
std::string unicode;
for (std::size_t i = 0; i < utf8.length();)
{
auto length = ucsz(utf8[i]);
switch (length)
{
case 4:
{
std::uint32_t val = 0;
for (std::size_t j = 0; j < 21; ++j)
{
auto idx = std::max(0, (int)(i + (3 - (j / 6))));
val |= ((utf8[idx] >> (j % 6)) & 1U) ? (1UL << j) : 0;
}
unicode.push_back(val >> 16);
unicode.push_back(val >> 8);
unicode.push_back(val >> 0);
++i;
++i;
++i;
break;
}
case 3:
{
std::uint32_t val = 0;
for (std::size_t j = 0; j < 16; ++j)
{
auto idx = std::max(0, (int)(i + (2 - (j / 6))));
val |= ((utf8[idx] >> (j % 6)) & 1U) ? (1UL << j) : 0;
}
unicode.push_back(val >> 8);
unicode.push_back(val >> 0);
++i;
++i;
break;
}
case 2:
{
std::uint16_t val = 0;
for (std::size_t j = 0; j < 11; ++j)
{
auto idx = std::max(0, (int)(i + (1 - (j / 6))));
val |= ((utf8[idx] >> (j % 6)) & 1U) ? (1UL << j) : 0;
}
unicode.push_back(val >> 8);
unicode.push_back(val >> 0);
++i;
break;
}
case 1:
{
unicode.push_back(utf8[i]);
break;
}
default:
{
break;
}
}
++i;
}
return unicode;
}