如何读取 C++ 中的重音字符并将它们与 isalnum 一起使用?

How can I read accented characters in C++ and use them with isalnum?

我正在用法语编程,因此,我需要使用重音字符。我可以使用输出它们 #include <locale>setlocale(LC_ALL, ""),但我读重音字符时似乎有问题。这是我用来说明问题的简单示例:

#include <locale>
#include <iostream>

using namespace std;

const string SymbolsAllowed = "+-*/%";

int main()
{
    setlocale(LC_ALL, "");    // makes accents printable

    // Traduction : Please write a string with accented characters
    // 'é' is shown correctly :
    cout << "Veuillez écrire du texte accentué : ";

    string accentedString;
    getline(cin, accentedString);

    // Accented char are not shown correctly :
    cout << "Accented string written : " << accentedString << endl;

    for (unsigned int i = 0; i < accentedString.length(); ++i)
    {
        char currentChar = accentedString.at(i);

        // The program crashes while testing if currentChar is alphanumeric.
        // (error image below) :
        if (!isalnum(currentChar) && !strchr(SymbolsAllowed.c_str(), currentChar))
        {
            cout << endl << "Character not allowed : " << currentChar << endl;
            system("pause");
            return 1;
        }
    }

    cout << endl << "No unauthorized characters were written." << endl;

    system("pause");
    return 0;
}

这是程序崩溃前的输出示例

Veuillez écrire du texte accentué : éèàìù
Accented string written : ʾS.?—

我注意到 Visual Studio 的调试器显示我编写的内容与它输出的内容不同:

[0] -126 '‚'    char
[1] -118 'Š'    char
[2] -123 '…'    char
[3] -115 ''     char
[4] -105 '—'    char

显示的错误似乎表明 只能使用 -1 到 255 之间的字符 但是,根据 ASCII table 我使用的重音字符的值在上面的例子中不要超过这个限制

这是弹出的错误对话框图片Error message: Expression: c >= -1 && c <= 255

有人可以告诉我我做错了什么或者给我一个解决方案吗?先感谢您。 :)

  1. char 在您的系统上(实际上,在许多系统上)是有符号类型,因此它的值范围是 -128 到 127。代码在 128 到 255 之间的字符看起来像如果它们存储在 char 中,则为负数,这实际上是您的调试器告诉您的内容:

    [0] -126 '‚'    char
    

    那是-126,不是126。换句话说,130或0x8C。

  2. isalnum 和朋友们将 int 作为参数,它(如错误消息所示)被限制为值 EOF(-1您的系统)和范围 0-255。 -126 不在此范围内。因此错误。您可以转换为 unsigned char,或者(可能更好,如果它适用于 Windows),使用两个参数 std::isalnum in <locale>

  3. 出于我完全无法理解的原因,Windows 似乎在 CP-437 but processing output in CP-1252 中提供控制台输入。这两个代码页的高半部分完全不同。因此,当您键入 é 时,它会作为 130 (0xC2) 从 CP-437 发送到您的程序,但是当您将相同的字符发送回控制台时,它会根据 CP-1252 作为(低) 打开单引号 (看起来很像逗号,但实际上不是)。所以那是行不通的。您需要让输入和输出位于同一代码页上。

  4. 我对Windows的了解不多,不过你可以在MS docs中找到一些有用的信息。该页面包含指向设置输入和输出代码页的 Windows 特定函数的链接。

  5. 有趣的是,您程序源代码中的重音字符似乎是 CP-1252,因为它们打印正确。如果您决定放弃代码页 1252——例如,通过采用 Unicode——您也必须修复您的源代码。

使用 is*to* 函数,您确实需要在将输入传递给函数之前将其转换为 unsigned char

if (!isalnum((unsigned char)currentChar) && !strchr(SymbolsAllowed.c_str(), currentChar)) {

当您使用它时,我建议您也不要使用 strchr,而改用这样的方式:

std::string SymbolsAllowed = "+-*/%";

if (... && SymbolsAllowed.find(currentChar) == std::string::npos)

当您使用它时,您可能应该忘记您甚至 听说过 exit 函数。你不应该在 C++ 中使用它。在这种情况下(从 main 退出)你应该 return。否则,抛出一个异常(如果你想退出程序,从那里捕获 main 和 return 中的异常)。

如果我写这篇文章,我的工作总体上会有所不同。 std::string 已经有一个函数可以完成你的循环试图完成的大部分工作,所以我会设置 symbolsAllowed 来包含 all 你想要的符号允许,然后搜索它不包含的任何内容:

// Add all the authorized characters to the string:
for (unsigned char a = 0; a < std::numeric_limits<unsigned char>::max(); a++)
    if (isalnum(a) || isspace(a)) // you probably want to allow spaces?
        symbolsAllowed += a;

// ...

auto pos = accentedString.find_first_not_of(symbolsAllowed);
if (pos != std::string::npos) {
    std::cout << "Character not allowed: " << accentedString[pos];
    return 1;
}