C / C ++ UTF-8大/小写转换

问题:有一种方法可以在一台机器上运行相应的测试用例而在另一台机器上运行失败(详情如下)。 我认为代码有问题,导致它在一台机器上偶然工作。 不幸的是我找不到问题。

请注意,std :: string和utf-8编码的使用是我没有实际影响的要求。 使用C ++方法会很好,但不幸的是我找不到任何东西。 因此使用C函数。

方法:

std::string firstCharToUpperUtf8(const string& orig) { std::string retVal; retVal.reserve(orig.size()); std::mbstate_t state = std::mbstate_t(); char buf[MB_CUR_MAX + 1]; size_t i = 0; if (orig.size() > 0) { if (orig[i] > 0) { retVal += toupper(orig[i]); ++i; } else { wchar_t wChar; int len = mbrtowc(&wChar, &orig[i], MB_CUR_MAX, &state); // If this assertion fails, there is an invalid multi-byte character. // However, this usually means that the locale is not utf8. // Note that the default locale is always C. Main classes need to set them // To utf8, even if the system's default is utf8 already. assert(len > 0 && len <= static_cast(MB_CUR_MAX)); i += len; int ret = wcrtomb(buf, towupper(wChar), &state); assert(ret > 0 && ret <= static_cast(MB_CUR_MAX)); buf[ret] = 0; retVal += buf; } } for (; i < orig.size(); ++i) { retVal += orig[i]; } return retVal; } 

考试:

 TEST(StringUtilsTest, firstCharToUpperUtf8) { setlocale(LC_CTYPE, "en_US.utf8"); ASSERT_EQ("Foo", firstCharToUpperUtf8("foo")); ASSERT_EQ("Foo", firstCharToUpperUtf8("Foo")); ASSERT_EQ("#foo", firstCharToUpperUtf8("#foo")); ASSERT_EQ("ßfoo", firstCharToUpperUtf8("ßfoo")); ASSERT_EQ("Éfoo", firstCharToUpperUtf8("éfoo")); ASSERT_EQ("Éfoo", firstCharToUpperUtf8("Éfoo")); } 

失败的测试(仅在两台机器之一上发生):

 Failure Value of: firstCharToUpperUtf8("ßfoo") Actual: "\xE1\xBA\x9E" "foo" Expected: "ßfoo" 

两台机器都安装了locale en_US.utf8。 然而,他们使用不同版本的libc。 它可以在GLIBC_2.14机器上运行,与编译的位置无关,在其他机器上不起作用,而只能在那里编译,因为否则它缺少正确的libc版本。

无论哪种方式,都有一台机器可以编译此代码并在失败时运行它。 代码有问题,我想知道是什么。 指向C ++方法(特别是STL),也会很棒。 由于其他外部要求,应避免使用Boost和其他库。

小案例s:ß; 大写s:ẞ。 你在断言中使用了大写版本吗? 看起来像glibg 2.14跟随实现pre unicode5.1没有大写版本的sharp s,而在另一台机器上libc使用unicode5.1ẞ= U1E9E …

也许有人会用它(可能用于测试)

有了这个你可以做简单的转换器:)没有额外的库:)

http://pastebin.com/fuw4Uizk

1482封信件

 Ь <> ь Э <> э Ю <> ю Я <> я Ѡ <> ѡ Ѣ <> ѣ Ѥ <> ѥ Ѧ <> ѧ Ѩ <> ѩ Ѫ <> ѫ Ѭ <> ѭ Ѯ <> ѯ Ѱ <> ѱ Ѳ <> ѳ Ѵ <> ѵ Ѷ <> ѷ Ѹ <> ѹ Ѻ <> ѻ Ѽ <> ѽ Ѿ <> ѿ Ҁ <> ҁ Ҋ <> ҋ Ҍ <> ҍ Ҏ <> ҏ Ґ <> ґ Ғ <> ғ Ҕ <> ҕ Җ <> җ Ҙ <> ҙ Қ <> қ Ҝ <> ҝ Ҟ <> ҟ Ҡ <> ҡ Ң <> ң 

下面的C ++ 11代码对我有用(暂时忽略了如何翻译尖锐s的问题 – 它保持不变。无论如何,它正逐渐从德国逐步淘汰)。

优化和大写第一个字母仅作为练习。

编辑:正如所指出的,codecvt似乎已被弃用。 但是,它应该保留在标准中,直到定义了合适的替代品。 请参阅不推荐的标头替换

 #include  #include  #include  std::locale const utf8("en_US.UTF-8"); // Convert UTF-8 byte string to wstring std::wstring to_wstring(std::string const& s) { std::wstring_convert > conv; return conv.from_bytes(s); } // Convert wstring to UTF-8 byte string std::string to_string(std::wstring const& s) { std::wstring_convert > conv; return conv.to_bytes(s); } // Converts a UTF-8 encoded string to upper case std::string tou(std::string const& s) { auto ss = to_wstring(s); for (auto& c : ss) { c = std::toupper(c, utf8); } return to_string(ss); } void test_utf8(std::ostream& os) { os << tou("foo" ) << std::endl; os << tou("#foo") << std::endl; os << tou("ßfoo") << std::endl; os << tou("Éfoo") << std::endl; } int main() { test_utf8(std::cout); } 

对于那个测试用例,你期望德国ß角色的大写版本是什么?

换句话说,你的基本假设是错误的。

请注意,评论中的维基百科指出:

Sharp s在拉丁字母的字母中几乎是独一无二的,因为它没有传统的大写forms(少数其他例子之一是kra,ĸ,它用于格陵兰语)。 这是因为它最初从未出现在德语文本中,而传统的德语印刷(使用blackletter)从未使用过全部大写字母。 使用全部大写时,当前的拼写规则要求用SS替换ß。[1] 但是,在2010年,在全部大写字母中编写地名时,在官方文件中强制使用它。[2]

因此,基本的测试案例,以尖锐的s作为首字母,违反了德国的规则。 我仍然认为我有一点,因为原始的海报前提是错误的,对于所有语言,字符串通常不能在大小写之间自由转换。

问题是您的语言环境没有断言是合规的,断言触发的语言环境是不合规的。

B.1.2 [ LC_CTYPE理由]中要求的技术报告N897 :

由于LC_CTYPE字符类基于C标准字符类定义,因此该类别不支持多字符元素。 例如,德语字符传统上被归类为小写字母。 没有相应的大写字母; 在适当的德语文本中,将被SS取代; 即两个字符。 这种转换超出了touppertolower关键字的范围。

本技术报告于12月25日至01年发布。 但根据: https : //en.wikipedia.org/wiki/Capital_%E1%BA%9E

2010年,在全部大写字母地名中,使用首都ẞ成为德国官方文件中的强制性要求

但是标准委员会没有重新讨论这个话题,所以技术上独立于德国政府所说的, toupper的标准化行为应该是不改变ß角色。

这与机器不一致的原因是setlocale

将指定的系统区域设置或其部分安装为新的C语言环境

所以它是不合规的系统区域设置, en_US.utf8指示toupper修改ß角色。 不幸的是,专门化ctype::clasic_tablectype上不可用,因此您无法修改该行为。 给你两个选择:

  1. 创建一个const map用于从每个可能的小写wchar_t转换为相应的大写wchar_t
  2. 像这样添加一张L'ß'的支票:

     int ret = wcrtomb(buf, wChar == L'ß' ? L'ẞ' : towupper(wChar), &state); 

实例