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

时间:2012-09-19 11:07:59

标签: c++ c utf-8 case-conversion

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

请注意,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<int>(MB_CUR_MAX));
      i += len;
      int ret = wcrtomb(buf, towupper(wChar), &state);
      assert(ret > 0 && ret <= static_cast<int>(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"

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

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

5 个答案:

答案 0 :(得分:9)

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

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

http://pastebin.com/fuw4Uizk

1482封信

实施例

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

答案 1 :(得分:3)

以下C ++ 11代码适用于我(无论是否为 关于如何翻译尖锐s的问题 - 那就是 保持不变。无论如何,它正逐渐从德国逐步淘汰。)

仅对第一个字母进行优化和大写 留下来作为练习。

编辑:正如所指出的,codecvt似乎已被弃用。但是,它应该保留在标准中,直到定义了合适的替代品。见Deprecated header <codecvt> replacement

if (*s1 == '\0')
    strcpy(s3, s2);
else if (*s2 == '\0')
    strcpy(s3, s1);

答案 2 :(得分:1)

对于该测试用例,您对德语ß字符的大写版本有何期待?

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

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

  

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

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

答案 3 :(得分:1)

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

答案 4 :(得分:0)

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

B.1.2 [LC_CTYPE基本原理]中要求

Technical Report 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<char>::clasic_tablectype<wchar_t>上不可用,因此您无法修改该行为。给你两个选择:

  1. 创建const map<wchar_t, wchar_t>,以便从每个可能的小写wchar_t转换为相应的大写wchar_t
  2. 添加L'ß'的支票,如下所示:

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