wstring::find() не работает с нелатинскими символами?

В моем коде есть строка широких символов (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 << L'ф'; работает правильно. Но это же

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

работает неправильно.

Это странно. Раньше у меня не было проблем с кодировкой, используя setlocale ().

4 ответа

Решение

Кодировка вашего исходного файла и кодировка среды выполнения могут сильно отличаться. C++ не дает никаких гарантий по этому поводу. Вы можете проверить это, выдав шестнадцатеричное значение строкового литерала:

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

До C++11 вы могли использовать не-ASCII символы в исходном коде, используя их шестнадцатеричные значения:

"\x05" "five"

C++11 добавляет возможность указывать их значение Unicode, которое в вашем случае будет

L"\u03A6"

Если вы используете полный C++11 (и ваша среда гарантирует, что они закодированы в UTF-*), вы можете использовать любой из char, char16_t, или же char32_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 (входная строка Unicode читается с использованием ReadConsoleW() Win32 API):

#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, этот ответ может помочь вам: /questions/11361677/spetsifikatsiya-ishodnoj-kodirovki-charset-v-msvc-naprimer-gcc-finput-charsetcharset/11361691#11361691

Другие вопросы по тегам