【问题标题】:C++ tolower on special characters such as üC++ 降低特殊字符,例如 ü
【发布时间】:2017-03-14 16:27:44
【问题描述】:

我无法使用 C++ 中的 tolower() 函数将字符串转换为小写。使用普通字符串,它可以按预期工作,但是特殊字符无法成功转换。

我如何使用我的功能:

string NotLowerCase = "Grüßen";
string LowerCase = "";
for (unsigned int i = 0; i < NotLowerCase.length(); i++) {
    LowerCase += tolower(NotLowerCase[i]);
    }

例如:

  1. 测试 -> 测试
  2. TeST2 -> 测试2
  3. Grüßen -> gr????en
  4. (§) -> ()

如您所见,3 和 4 没有按预期工作

我该如何解决这个问题?我必须保留特殊字符,但要小写。

【问题讨论】:

  • 您是否意识到这是不可能正确的,因为ß 转换为SS,而SS 可能转换为@987654325 @ 或 ss 取决于上下文?
  • 是的,我理解并且已经删除了我的评论,很好的答案伙计们,谢谢继续。 p.s.当这种情况没有发生并且像原来一样保持正常的“单词”时,使用什么安全的语言?例如en_US.iso88591?
  • 为什么人们总是把完全正常的字母称为“特殊字符”?

标签: c++ character-encoding htmlspecialchars


【解决方案1】:

tolower 的示例代码(如下)显示了您如何解决此问题;你必须使用默认的“C”语言环境以外的东西。

#include <iostream>
#include <cctype>
#include <clocale>

int main()
{
    unsigned char c = '\xb4'; // the character Ž in ISO-8859-15
                              // but ´ (acute accent) in ISO-8859-1 

    std::setlocale(LC_ALL, "en_US.iso88591");
    std::cout << std::hex << std::showbase;
    std::cout << "in iso8859-1, tolower('0xb4') gives "
              << std::tolower(c) << '\n';
    std::setlocale(LC_ALL, "en_US.iso885915");
    std::cout << "in iso8859-15, tolower('0xb4') gives "
              << std::tolower(c) << '\n';
}

您还可以将 std::string 更改为 std::wstring,这是许多 C++ 实现中的 Unicode。

wstring NotLowerCase = L"Grüßen";
wstring LowerCase;
for (auto&& ch : NotLowerCase) {
    LowerCase += towlower(ch);
    }

Microsoft 的指导是“Normalize strings to uppercase”,因此您可以改用touppertowupper

请记住,逐个字符的转换可能不适用于某些语言。例如,使用德国所说的德语,将 Grüßen 全部大写将其变为 GRÜESSEN(尽管现在有 capital )。还有许多其他“问题”,例如组合字符;如果您正在使用字符串进行真正的“生产”工作,那么您真的需要一种完全不同的方法。

最后,C++ 对管理语言环境提供了更复杂的支持,详情请参阅&lt;locale&gt;

【讨论】:

  • 请注意,这适用于 ISO-8859-*,但不适用于 Unicode。由于它被标记为“htmlspecialcharacters”,因此 unicode 是一个合理的假设。
  • 哇,这个 toupper -> towupper 做到了。 (我当然把它改成更低了,但它现在似乎可以工作)谢谢你的支持!
  • @Ðаn:确实。公平地说,tolower 已经在对字符集做出假设。中文是经典的反例。 ISO-8859 描述了一组 8 位字符集,它们一起涵盖了大多数小写字母有意义的字母。但是对于 UTF-8,事情突然变得复杂了很多。不要让我开始了解特定于语言环境的案例规则;我每条评论只有 600 个字符。不过要记住一个简短的例子:ß=>SS。即使在 8859-1 中,使用 char toupper(char) 也无法做到这一点。字符串长度随大写而变化!
  • @TVAvanHesteren 您不能真正支持多种语言,除非您根据具体情况支持它们各自的怪癖。您可以支持在多种语言中使用的字符,但前提是您不以任何方式操纵这些字符。将单词更改为大写然后再变为小写可以是deadly
  • 那么,你建议如何解决这个问题或解决这个问题?
【解决方案2】:

我认为最便携的方法是使用用户选择的语言环境,这是通过将语言环境设置为""(空字符串)来实现的。

std::locale::global(std::locale("")); 

这会将语言环境设置为程序运行时使用的任何内容,并影响在多字节和宽字符串字符之间转换的标准字符转换例程(std::mbsrtowcsstd::wcsrtombs)。

然后您可以使用这些函数将系统/用户选择的多字节字符(例如UTF-8)转换为系统标准宽字符代码,这些代码可用于std::tolower等对一个字符进行操作的函数中一次。

这很重要,因为像 UTF-8 这样的多字节字符集不能使用像 std::tolower() 这样的单字符操作来转换。

一旦您将宽字符串版本转换为大写/小写,就可以将其转换回系统/用户多字节字符集以打印到控制台。

// Convert from multi-byte codes to wide string codes
std::wstring mb_to_ws(std::string const& mb)
{
    std::wstring ws;
    std::mbstate_t ps{};
    char const* src = mb.data();

    std::size_t len = 1 + mbsrtowcs(0, &src, 3, &ps);

    ws.resize(len);
    src = mb.data();

    mbsrtowcs(&ws[0], &src, ws.size(), &ps);

    if(src)
        throw std::runtime_error("invalid multibyte character after: '"
            + std::string(mb.data(), src) + "'");

    ws.pop_back();

    return ws;
}

// Convert from wide string codes to multi-byte codes
std::string ws_to_mb(std::wstring const& ws)
{
    std::string mb;
    std::mbstate_t ps{};
    wchar_t const* src = ws.data();

    std::size_t len = 1 + wcsrtombs(0, &src, 0, &ps);

    mb.resize(len);
    src = ws.data();

    wcsrtombs(&mb[0], &src, mb.size(), &ps);

    if(src)
        throw std::runtime_error("invalid wide character");

    mb.pop_back();

    return mb;
}

int main()
{
    // set locale to the one chosen by the user
    // (or the one set by the system default)
    std::locale::global(std::locale(""));

    try
    {
        string NotLowerCase = "Grüßen";

        std::cout << NotLowerCase << '\n';

        // convert system/user multibyte character codes
        // to wide string versions
        std::wstring ws1 = mb_to_ws(NotLowerCase);
        std::wstring ws2;

        for(unsigned int i = 0; i < ws1.length(); i++) {
            // use the system/user locale
            ws2 += std::tolower(ws1[i], std::locale("")); 
        }

        // convert wide string character codes back
        // to system/user multibyte versions
        string LowerCase = ws_to_mb(ws2);

        std::cout << LowerCase << '\n';
    }
    catch(std::exception const& e)
    {
        std::cerr << e.what() << '\n';
        return EXIT_FAILURE;
    }
    catch(...)
    {
        std::cerr << "Unknown exception." << '\n';
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

代码没有经过大量测试

【讨论】:

    【解决方案3】:

    使用 ASCII

    string NotLowerCase = "Grüßen";
    string LowerCase = "";
    for (unsigned int i = 0; i < NotLowerCase.length(); i++) {
        if(NotLowerCase[i]<65||NotLowerCase[i]>122)
        {
            LowerCase+='?';
        }
        else
            LowerCase += tolower(NotLowerCase[i]);
    }
    

    【讨论】:

    • 我需要问题中所述的小写特殊字符。这只是用未请求的“有效”问号替换它们。感谢您的输入
    猜你喜欢
    • 1970-01-01
    • 2013-03-13
    • 2012-05-20
    • 2020-10-25
    • 1970-01-01
    • 2015-10-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多