MSVCはsに格納wchar_t
しwstring
ます。これらは、Unicode 16ビットワード、または実際には他のものとして解釈できます。
Unicode文字またはグリフにアクセスする場合は、上記の生の文字列をUnicode標準で処理する必要があります。また、一般的なコーナーケースを壊さずに処理したいと思うかもしれません。
これがそのようなライブラリのスケッチです。これは、メモリ効率の約半分ですが、のユニコードグリフへのインプレースアクセスを提供しますstd::string
。それはまともなクラスを持っていることに依存してarray_view
いますが、とにかくそれらの1つを書きたいと思います:
struct unicode_char : array_view<wchar_t const> {
using array_view<wchar_t const>::array_view<wchar_t const>;
uint32_t value() const {
if (size()==1)
return front();
Assert(size()==2);
if (size()==2)
{
wchar_t high = front()-0xD800;
wchar_T low = back()-0xDC00;
return (uint32_t(high)<<10) + uint32_t(low);
}
return 0; // error
}
static bool is_high_surrogate( wchar_t c ) {
return (c >= 0xD800 && c <= 0xDBFF);
}
static bool is_low_surrogate( wchar_t c ) {
return (c >= 0xDC00 && c <= 0xDFFF);
}
static unicode_char extract( array_view<wchar_t const> raw )
{
if (raw.empty())
return {};
if (raw.size()==1)
return raw;
if (is_high_surrogate(raw.front()) && is_low_surrogate(*std::next(raw.begin())))
return {raw.begin(), raw.begin()+2);
return {raw.begin(), std::next(raw.begin())};
}
};
static std::vector<unicode_char> as_unicode_chars( array_view<wchar_t> raw )
{
std::vector<unicode_char> retval;
retval.reserve( raw.size() ); // usually 1:1
while(!raw.empty())
{
retval.push_back( unicode_char::extract(raw) );
Assert( retval.back().size() <= raw.size() );
raw = {raw.begin() + retval.back().size(), raw.end()};
}
return retval;
}
struct unicode_glyph {
std::array< unicode_char, 3 > buff;
std::size_t count=0;
unicode_char const* begin() const {
return buff.begin();
}
unicode_char const* end() const {
return buff.begin()+count;
}
std::size_t size() const { return count; }
bool empty() { return size()==0; }
unicode_char const& front() const { return *begin(); }
unicode_char const& back() const { return *std::prev(end()); }
array_view< unicode_char const > chars() const { return {begin(), end()}; }
array_view< wchar_t const > wchars() const {
if (empty()) return {};
return { front().begin(), back().end() };
}
void append( unicode_char next ) {
Assert(count<3);
buff[count++] = next;
}
unicode_glyph() {}
static bool is_diacrit(unicode_char c) const {
auto v = c.value();
return is_diacrit(v);
}
static bool is_diacrit(uint32_t v) const {
return
((v >= 0x0300) && (v <= 0x0360))
|| ((v >= 0x1AB0) && (v <= 0x1AFF))
|| ((v >= 0x1DC0) && (v <= 0x1DFF))
|| ((v >= 0x20D0) && (v <= 0x20FF))
|| ((v >= 0xFE20) && (v <= 0xFE2F));
}
static size_t diacrit_count(unicode_char c) const {
auto v = c.value();
if (is_diacrit(v))
return 1 + ((v >= 0x035C)&&(v<=0x0362));
else
return 0;
}
static unicode_glyph extract( array_view<const unicode_char> raw ) {
unicode_glyph retval;
if (raw.empty())
return retval;
if (raw.size()==1)
{
retval.append(raw.front());
return retval;
}
retval.count = diacrit_count( *std::next(raw.begin()) )+1;
std::copy( raw.begin(), raw.begin()+retval.count, retval.buff.begin() );
return retval;
}
};
static std::vector<unicode_glyph> as_unicode_glyphs( array_view<unicode_char> raw )
{
std::vector<unicode_glyph> retval;
retval.reserve( raw.size() ); // usually 1:1
while(!raw.empty())
{
retval.push_back( unicode_glyph::extract(raw) );
Assert( retval.back().size() <= raw.size() );
raw = {raw.begin() + retval.back().size(), raw.end()};
}
return retval;
}
static std::vector<unicode_glyph> as_unicode_glyphs( array_view<wchar_t> raw )
{
return as_unicode_glyphs( as_unicode_chars( raw ) );
}
よりスマートなコードは、ある種のファクトリイテレータを使用してオンザフライでunicode_char
sとsを生成します。unicode_glyph
よりコンパクトな実装では、前の終了ポインターと次の開始ポインターが常に同一であるという事実を追跡し、それらを一緒にエイリアスします。もう1つの最適化は、ほとんどのグリフが1文字であるという仮定に基づいて、グリフに小さなオブジェクトの最適化を使用し、2文字の場合は動的割り当てを使用することです。
私はCGJを標準の発音区別符号として扱い、二重発音区別符号は1つ(ユニコード)を形成する3文字のセットとして扱いますが、半発音区別符号は物事を1つのグリフにマージしないことに注意してください。これらはすべて疑わしい選択です。
これは不眠症の発作で書かれました。少なくともある程度は機能することを願っています。