C++ 20では、UTF-8にchar8_t
およびstd::u8string
が追加されました。ただし、std::cout
のUTF-8バージョンはなく、OS APIは主にchar
および実行文字セットを想定しています。したがって、UTF-8と実行文字セットを変換する方法が必要です。
私は char8_t論文 を読み直していましたが、UTF-8とECSの間で変換する唯一の方法はstd::c8rtomb
関数とstd::mbrtoc8
関数を使用することです。ただし、そのAPIは非常に混乱します。誰かがサンプルコードを提供できますか?
2020年の更新MAR 17
_std::c8rtomb
_および_std::mbrtoc8
_はまだ提供されていません。
2019年11月
_std::c8rtomb
_および_std::mbrtoc8
_は、「3」によって作成された将来のC++ 20対応コンパイラによって、実行エンコーディングとUTF-8間の変換を可能にするために、まだ提供されていません。それらはC++ 20標準で記述されています。
主観的かもしれませんが、c8rtomb()
は私にとって「扱いにくい」インターフェースではありません。
_// g++ prog.cc -std=gnu++2a
// clang++ prog.cc -std=c++2a
#include <stdio.h>
#include <clocale>
#ifndef __clang__
#include <cuchar>
#else
// clang has no <cuchar>
#include <uchar.h>
#endif
#include <climits>
template<size_t N>
void u32sample( const char32_t (&str32)[N] )
{
#ifndef __clang__
std::mbstate_t state{};
#else
mbstate_t state{};
#endif
char out[MB_LEN_MAX]{};
for(char32_t const & c : str32)
{
#ifndef __clang__
/*std::size_t rc =*/ std::c32rtomb(out, c, &state);
#else
/* std::size_t rc =*/ ::c32rtomb(out, c, &state);
#endif
printf("%s", out ) ;
}
}
#ifdef __STDC_UTF_8__
template<size_t N>
void u8sample( const char8_t (& str8)[N])
{
std::mbstate_t state{};
char out[MB_LEN_MAX]{};
for(char8_t const & c : str8)
{
/* std::size_t rc = */ std::c8rtomb(out, c, &state);
printf("%s", out ) ;
}
}
#endif // __STDC_UTF_8__
int main () {
std::setlocale(LC_ALL, "en_US.utf8");
#ifdef __linux__
printf("\nLinux like OS, ") ;
#endif
printf(" Compiler %s\n", __VERSION__ ) ;
printf("\nchar32_t *, Converting to 'char *', and then printing --> " ) ;
u32sample( U"ひらがな" ) ;
#ifdef __STDC_UTF_8__
printf("\nchar8_t *, Converting to 'char *', and then printing --> " ) ;
u8sample( u8"ひらがな" ) ;
#else
printf("\n\n__STDC_UTF_8__ is not defined, can not use char8_t");
#endif
printf("\n\nDone ..." ) ;
return 42;
}
_
今日の時点でコンパイルされない行をコメントアウトして文書化しました。
C++ 20に準拠しているはずのコードを次に示します。現在(2020年3月)には、このペーパーで定義されている変換関数を実装しているコンパイラがないため、現在実装されているものに制約されず、C++ 20の完全な仕様を使用することにしました。したがって、std::basic_string
またはstd::basic_string_view
コードユニットの範囲を取得します。戻り値は一般的ではありませんが、代わりに出力範囲を取るように変更するのは簡単です。これは読者への演習として残されています。
/// \brief Converts the range of UTF-8 code units to execution encoding.
/// \tparam R Type of the input range.
/// \param[in] input Input range.
/// \return std::string in the execution encoding.
/// \throw std::invalid_argument If input sequence is ill-formed.
/// \note This function depends on the global locale.
template <std::ranges::input_range R>
requires std::same_as<std::ranges::range_value_t<R>, char8_t>
std::string ToECSString(R&& input)
{
std::string output;
char temp_buffer[MB_CUR_MAX];
std::mbstate_t mbstate{};
auto i = std::ranges::begin(input);
auto end = std::ranges::end(input);
for (; i != end; ++i)
{
std::size_t result = std::c8rtomb(temp_buffer, *i, &mbstate);
if (result == -1)
{
throw std::invalid_argument{"Ill-formed UTF-8 sequence."};
}
output.append(temp_buffer, temp_buffer + result);
}
return output;
}
/// \brief Converts the input range of code units in execution encoding to
/// UTF-8.
/// \tparam R Type of the input range.
/// \param[in] input Input range.
/// \return std::u8string containing UTF-8 code units.
/// \throw std::invalid_argument If input sequence is ill-formed or does not end
/// at the scalar value boundary.
/// \note This function depends on the global C locale.
template <std::ranges::input_range R>
requires std::same_as<std::ranges::range_value_t<R>, char>
std::u8string ToUTF8String(R&& input)
{
std::u8string output;
char8_t temp_buffer;
std::mbstate_t mbstate{};
std::size_t result;
auto i = std::ranges::begin(input);
auto end = std::ranges::end(input);
while (i != end)
{
result = std::mbrtoc8(&temp_buffer, std::to_address(i), 1, &mbstate);
switch (result)
{
case 0:
{
++i;
break;
}
case std::size_t(-3):
{
break;
}
case std::size_t(-2):
{
++i;
break;
}
case std::size_t(-1):
{
throw std::invalid_argument{"Invalid input sequence."};
}
default:
{
std::ranges::advance(i, result);
break;
}
}
if (result != std::size_t(-2))
{
output.append(1, temp_buffer);
}
}
if (result == -2)
{
throw std::invalid_argument{
"Code unit sequence does not end at the scalar value "
"boundary."};
}
return output;
}
/// \brief Converts the contiguous range of code units in execution encoding to
/// UTF-8.
/// \tparam R Type of the contiguous range.
/// \param[in] input Input range.
/// \return std::u8string containing UTF-8 code units.
/// \throw std::invalid_argument If input sequence is ill-formed or does not end
/// at the scalar value boundary.
/// \note This function depends on the global C locale.
template <std::ranges::contiguous_range R>
requires std::same_as<std::ranges::range_value_t<R>, char>
std::u8string ToUTF8String(R&& input)
{
std::u8string output;
char8_t temp_buffer;
std::mbstate_t mbstate{};
std::size_t offset = 0;
std::size_t size = std::ranges::size(input);
while (offset != size)
{
std::size_t result = std::mbrtoc8(&temp_buffer,
std::ranges::data(input) + offset, size - offset, &mbstate);
switch (result)
{
case 0:
{
++offset;
break;
}
case std::size_t(-3):
{
break;
}
case std::size_t(-2):
{
throw std::invalid_argument{
"Input sequence does not end at the scalar value "
"boundary."};
}
case std::size_t(-1):
{
throw std::invalid_argument{"Invalid input sequence."};
}
default:
{
offset += result;
break;
}
}
output.append(1, temp_buffer);
}
return output;
}
毎年のCppCon大会(2018や2019など)でC++当局から寄せられた一般的な答えは、独自のUTF8ライブラリを選択する必要があるというものでした。好きなものを選ぶだけであらゆる種類の味があります。 C++側のUnicodeに対する理解とサポートはまだ恥ずかしいものです。
一部の人々はC++ 23に何かがあると思いますが、今のところ公式のワーキンググループさえありません。