MSVC 存储wchar_t
在wstring
s 中。这些可以解释为 unicode 16 位字,或者其他任何东西。
如果您想访问 unicode 字符或字形,则必须按 unicode 标准处理所述原始字符串。您可能还想在不中断的情况下处理常见的极端情况。
这是这样一个图书馆的草图。它的内存效率大约是它可能的一半,但它确实让您可以就地访问std::string
. 它依赖于有一个像样的array_view
课程,但无论如何你想写一个:
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
更紧凑的实现将跟踪这样一个事实,即前一个的结束指针和下一个的开始指针总是相同的,并将它们别名在一起。另一种优化是基于大多数字形是一个字符的假设对字形使用小对象优化,如果它们是两个则使用动态分配。
请注意,我将 CGJ 视为标准变音符号,将双变音符号视为一组 3 个字符组成一个(unicode),但半变音符号不会将事物合并到一个字形中。这些都是值得怀疑的选择。
这是在一次失眠症中写的。希望它至少在某种程度上有效。