【问题标题】:wstring::find() doesn't work with non-latin symbols?wstring::find() 不适用于非拉丁符号?
【发布时间】:2013-03-25 08:42:00
【问题描述】:

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

我使用 find() 函数:

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

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

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

是这个功能的问题吗? 还是我做错了什么?

UPD

我使用 MinGW 并将源代码保存为 UTF-8。 我还使用setlocale(LC_ALL, ""); 设置语言环境。 相同的代码wcout &lt;&lt; L'ф'; 可以协同工作。 但是一样

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

工作不正确。

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

【问题讨论】:

标签: c++ stl wstring setlocale


【解决方案1】:

源文件的编码和执行环境的编码可能大相径庭。 C++ 对此不做任何保证。您可以通过输出字符串文字的十六进制值来检查这一点:

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

在 C++11 之前,您可以通过使用其十六进制值在源代码中使用非 ASCII 字符:

"\x05" "five"

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

L"\u03A6"

如果您要使用完整的 C++11(并且您的环境确保这些以 UTF-* 编码),您可以使用 charchar16_tchar32_t 中的任何一个,然后执行以下操作:

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

【讨论】:

  • 我使用 MinGW、Windows 控制台并将我的源代码保存为 UTF-8。但我调用 setlocale(LC_ALL, "");在任何输入/输出之前。我认为,它可以防止类似的问题,不是吗?
  • 没有。问题不在于正在运行的程序的全局 C 语言环境,而是编译器对构成源文件中 phi 字符的字节的翻译。这种翻译是实现定义的,因此不可移植。
【解决方案2】:

您必须设置控制台的编码。

这行得通:

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

【讨论】:

  • 当我在 Windows 控制台中尝试此操作并键入一些输入字符串并按 enter 时,程序就会挂起(我必须按 CTRL+C 才能恢复)。
  • 我使用的是 Windows 7 和 Visual Studio 2012,它运行良好。
  • 我正在使用 Windows 7 和 VS2010...也许 VS2010 中存在一个在 2012 年修复的错误(?)。
【解决方案3】:

这可能是编码问题。 wcin 使用与编译器/源代码不同的编码。尝试在控制台/wcin 中输入 ф - 它会起作用。尝试通过 wcout 打印 ф -- 它会显示不同的字符或根本不显示字符。

没有独立于平台的方法来规避这一点,但如果您在 Windows 上,则可以使用chchp 命令行命令或以编程方式使用SetConsoleCP()(输入)和SetConsoleOutputCP()(输出)。

您还可以更改源文件/编译器的编码。如何完成取决于您的编辑器/编译器。如果您使用的是 MSVC,这个答案可能会对您有所帮助:https://stackoverflow.com/a/1660901/2128694

【讨论】:

    【解决方案4】:

    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
    

    【讨论】:

      猜你喜欢
      • 2019-07-11
      • 2012-11-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-08
      • 1970-01-01
      • 2015-06-26
      • 2011-02-15
      相关资源
      最近更新 更多