wstring::find() 不适用于非拉丁符号?

wstring::find() doesn't work with non-latin symbols?

本文关键字:丁符号 符号 适用于 find 不适用 wstring      更新时间:2023-10-16

我的代码中有一个宽字符字符串(std::wstring),我需要在其中搜索宽字符。

我使用find()函数:

    wcin >> str;
    wcout << ((str.find(L'ф') != wstring::npos)? L"EXIST":L"NONE");

L'ф'是一个西里尔字母。

但是find()在同一个调用中总是返回npos。在使用拉丁字母的情况下,find()可以正常工作。

这个函数有问题吗?还是我做错了什么?

乌利希期刊指南

我使用MinGW并以UTF-8格式保存源代码。我也用setlocale(LC_ALL, "");设置区域设置。代码相同的wcout << L'ф';工作正常。但同样的

wchar_t w;
wcin >> w;
wcout << w;

正确工作。

真奇怪。早些时候,我使用setlocale()对编码没有任何问题。

源文件的编码和执行环境的编码可能大不相同。c++对这些都没有保证。您可以通过输出字符串字面值的十六进制值来检查:

std::wcout << std::hex << L"ф";

在c++ 11之前,您可以在源代码中使用非ascii字符的十六进制值:

"x05" "five"

c++ 11增加了指定它们的Unicode值的能力,在你的情况下就是

L"u03A6"

如果您要使用完整的c++ 11(并且您的环境确保这些是用UTF-*编码的),您可以使用char, char16_tchar32_t中的任何一个,并执行:

const char* phi_utf8 = "u03A6";
const char16_t* phi_utf16 = u"u03A6";
const char32_t* phi_utf16 = U"u03A6";

必须设置控制台的编码。

如此:

#include <iostream>
#include <string>
#include <io.h>
#include <fcntl.h>
#include <stdio.h>
using namespace std;
int main()
{       
    _setmode(_fileno(stdout), _O_U16TEXT);
    _setmode(_fileno(stdin), _O_U16TEXT);
    wstring str;
    wcin >> str;
    wcout << ((str.find(L'ф') != wstring::npos)? L"EXIST":L"NONE");
    system("pause");
    return 0;
}

std::wstring::find()工作正常。但是您必须正确读取输入字符串。

以下代码在Windows控制台上运行良好(使用 ReadConsoleW() Win32 API读取输入的Unicode字符串):

#include <exception>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>
#include <windows.h>
using namespace std;
class Win32Error : public runtime_error
{
public:
    Win32Error(const char* message, DWORD error)
        : runtime_error(message)
        , m_error(error)
    {}
    DWORD Error() const
    {
        return m_error;
    }
private:
    DWORD m_error;
};
void ThrowLastWin32(const char* message)
{
    const DWORD error = GetLastError();
    throw Win32Error(message, error);
}
void Test()
{
    const HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
    if (hStdIn == INVALID_HANDLE_VALUE)
        ThrowLastWin32("GetStdHandle failed.");
    static const int kBufferLen = 200;
    wchar_t buffer[kBufferLen];
    DWORD numRead = 0;
    if (! ReadConsoleW(hStdIn, buffer, kBufferLen, &numRead, nullptr))
        ThrowLastWin32("ReadConsoleW failed.");
    const wstring str(buffer, numRead - 2);
    static const wchar_t kEf = 0x0444;
    wcout << ((str.find(kEf) != wstring::npos) ? L"EXIST" : L"NONE");
}
int main()
{
    static const int kExitOk = 0;
    static const int kExitError = 1;
    try
    {
        Test();
        return kExitOk;
    }    
    catch(const Win32Error& e)
    {
        cerr << "n*** ERROR: " << e.what() << 'n';
        cerr << "    (GetLastError returned " << e.Error() << ")n";
        return kExitError;
    }
    catch(const exception& e)
    {
        cerr << "n*** ERROR: " << e.what() << 'n';
        return kExitError;
    }        
}
输出:

C:TEMP>test.exe
abc
NONE
C:TEMP>test.exe
abcфabc
EXIST

这可能是编码问题。wcin使用不同于编译器/源代码的编码。试着在控制台/wcin中输入这个参数——它会工作的。试着通过wcout打印这个字符——它会显示一个不同的字符,或者根本不显示字符。

没有平台独立的方法来绕过这个问题,但是如果您在windows上,您可以手动更改控制台编码,可以使用chchp命令行命令或通过编程方式使用SetConsoleCP()(输入)和SetConsoleOutputCP()(输出)。

你也可以改变你的源文件/编译器的编码。如何做到这一点取决于您的编辑器/编译器。如果您正在使用MSVC,下面的答案可能会对您有所帮助:https://stackoverflow.com/a/1660901/2128694