MSVC хранит wchar_t
в wstring
с. Их можно интерпретировать как 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 и unicode_glyph
s на лету с помощью какого-либо фабричного итератора. Более компактная реализация будет отслеживать тот факт, что конечный указатель предыдущего и начальный указатель следующего всегда идентичны, и объединять их вместе. Другой оптимизацией может быть использование оптимизации небольших объектов для глифа, основанное на предположении, что большинство глифов представляют собой один символ, и использование динамического распределения, если их два.
Обратите внимание, что я рассматриваю CGJ как стандартный диакрит, а двойные диакриты — как набор из 3 символов, которые образуют один (юникод), но полудиакриты не объединяют вещи в один глиф. Это все сомнительные варианты.
Это было написано в приступе бессонницы. Надеюсь, это хоть немного сработает.
person
Yakk - Adam Nevraumont
schedule
24.09.2014
wstring
действительно отслеживает количество элементов, как вы сказали, которые он должен знать для целей распределения, и полностью не зависит от кодировки. Но элементы != количество кодовых точек != количество глифов, вы должны отслеживать другие меры длины. - person Ben Voigt   schedule 26.10.2010wchar_t
определяется как количество элементов, предшествующих'\0'
? Иstring
, иwstring
поддерживают строки, содержащие нулевые символы. - person David Rodríguez - dribeas   schedule 26.10.2010wstring
во второй части вопроса. Первая часть простаяwchar_t*
. Илиwchar_t s[constant];
, если быть точным. - person Šimon Tóth   schedule 26.10.2010wstring::size
должен возвращать числоwchar_t
в строке. Если MS, между прочим, говорит, что на самом деле это UTF-16, а не UCS-2, это не имеет значения. - person Steve Jessop   schedule 27.10.2010wcslen
? Как вы на самом деле определяете длину строки в Windows? - person Šimon Tóth   schedule 27.10.2010wcslen
возвращает числоwchar_t
перед 0. Поддержка Windows для обработки суррогатных пар, AFAIK, мусор. Вам, вероятно, лучше с UTF-8 и_mbslen
. - person Steve Jessop   schedule 27.10.2010wchar_t
. Windows API для меня неактуален (то же самое касается внешних кодировок). - person Šimon Tóth   schedule 27.10.2010