繁体   English   中英

Microsoft如何处理UTF-16在其C ++标准库实现中是可变长度编码的事实

[英]How does Microsoft handle the fact that UTF-16 is a variable length encoding in their C++ standard library implementation

标准中间接禁止使用可变长度编码。

所以我有几个问题:

如何处理标准的以下部分?

17.3.2.1.3.3宽字符序列

宽字符序列是一个数组对象(8.3.4)A,可以声明为TA [N],其中T是wchar_t(3.9.1)类型,可选择由const或volatile的任意组合限定。 数组的初始元素定义了内容,包括由某个谓词确定的元素。 字符序列可以由指定其第一元素的指针值S指定。

NTWCS的长度是终止空宽字符之前的元素数。 空NTWCS的长度为零。

问题:

basic_string<wchar_t>

  • operator[]如何实现以及它返回了什么?
    • standard: If pos < size(), returns data()[pos]. Otherwise, if pos == size(), the const version returns charT(). Otherwise, the behavior is undefined. If pos < size(), returns data()[pos]. Otherwise, if pos == size(), the const version returns charT(). Otherwise, the behavior is undefined.
  • size()返回元素的数量还是字符串的长度?
    • standard: Returns: a count of the number of char-like objects currently in the string.
  • resize()如何工作?
    • 与标准无关,它是做什么的
  • insert()erase()和其他位置的处理方式如何?

cwctype

  • 几乎所有东西都在这里。 如何处理变量编码?

cwchar

  • getwchar()显然无法返回整个平台字符,那么这是如何工作的呢?

加上所有其余的角色功能(主题是相同的)。

编辑:我将打开奖金以获得一些确认。 我希望得到一些明确的答案或至少更明确的投票分配。

编辑:这开始变得毫无意义。 这充满了完全相互矛盾的答案。 你们有些人谈论外部编码(我不关心那些,UTF-8编码将仍然存储为UTF-16一旦读入字符串,输出相同),其余只是相互矛盾。 : - /

以下是Microsoft的STL实现如何处理可变长度编码:

basic_string<wchar_t>::operator[])(可以basic_string<wchar_t>::operator[])(返回低代理或高代理。

basic_string<wchar_t>::size()返回wchar_t对象的数量。 代理对(一个Unicode字符)使用两个wchar_t,因此会增加两个大小。

basic_string<wchar_t>::resize()可以截断代理项对中间的字符串。

basic_string<wchar_t>::insert()可以插入代理项对的中间。

basic_string<wchar_t>::erase()可以删除代理对的一半。

通常,模式应该是明确的:STL不假定std::wstring是UTF-16,也不强制它仍然是UTF-16。

STL将字符串作为字符数组的包装器处理,因此STL字符串上的size()或length()将告诉您它包含多少个char或wchar_t元素,而不一定是字符串中可打印字符的数量。

假设您正在讨论wstring类型,则不会处理编码 - 它只处理wchar_t元素而不知道编码的任何内容。 这只是wchar_t的序列。 您需要使用其他功能的功能来处理编码问题。

两件事情:

  1. 没有“Microsoft STL实现”。 Visual C ++附带的C ++标准库由Dinkumware许可。
  2. 当前的C ++标准对Unicode及其编码形式一无所知。 std :: wstring只是wchar_t单元的容器,在Windows上恰好是16位。 实际上,如果要将UTF-16编码的字符串存储到wstring中,只需要考虑到您实际存储的是代码单元而不是代码点。

MSVC将wchar_t存储在wstring 这些可以解释为unicode 16位字,或者其他任何东西。

如果要访问unicode字符或字形,则必须按unicode标准处理所述原始字符串。 您可能还希望在不中断的情况下处理常见的角落情况。

这是一个这样的图书馆的草图。 它大约是内存效率的一半,但它确实可以让你在std::string就地访问unicode字形。 它依赖于一个像样的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_glyph生成unicode_charunicode_glyph 更紧凑的实现将跟踪前一个的结束指针和下一个的开始指针始终相同的事实,并将它们混合在一起。 另一个优化是基于假设大多数字形是一个字符而在字形上使用小对象优化,并且如果它们是两个则使用动态分配。

请注意,我将CGJ视为标准的diacrit,并将double-diacrits视为一组3个字符组成一个(unicode),但是half-diacrits不会将事物合并为一个字形。 这些都是值得怀疑的选择。

这是在一阵失眠症中写的。 希望它至少在某种程度上有效。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM