C++ tolower 特殊字符,如 ü

C++ tolower on special characters such as ü

我在使用 C++ 中的 tolower() 函数将字符串转换为小写时遇到问题。对于普通字符串,它按预期工作,但特殊字符未成功转换。

我如何使用我的函数:

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

例如:

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

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

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

来自 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", so you might use toupper or towupper

请记住,逐个字符的转换可能不适用于某些语言。例如,使用德国的德语,将 Grüßen 全部大写变成 GRÜESSEN(尽管现在有一个 capital ).还有许多其他 "problems" 这样的组合字符;如果您真正 "production" 使用字符串,您真的需要一种完全不同的方法。

最后,C++ 对管理区域设置有更完善的支持,请参阅 <locale> 了解详细信息。

使用 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]);
}

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

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

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

然后你可以使用这些函数将system/user选择的多字节字符(如UTF-8)转换为系统标准的宽字符代码,可以在[=14]等函数中使用=] 一次对一个字符进行操作。

这很重要,因为 UTF-8 等多字节字符集无法使用 std::tolower().

等单字符操作进行转换

将宽字符串版本转换为 upper/lower 大小写后,可以将其转换回 system/user 多字节字符集以打印到控制台。

// 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;
}

代码未经过严格测试