Невозможно извлечь символы Юникода из C++ std::string
Я хочу прочитать C++ std::string, затем передать эту std:: string функции, которая будет ее анализировать, а затем извлечь из нее символы Unicode и простые символы ASCII.
Я просмотрел много руководств в Интернете, но все они упоминали, что стандартный C++ не полностью поддерживает формат Unicode. Многие из них упомянули об использовании ICU C++.
Это моя программа на C++ для понимания основных функций, перечисленных выше. Он читает необработанную строку, преобразует ее в строку ICU Unicode и печатает, что:
#include <iostream>
#include <string>
#include "unicode/unistr.h"
int main()
{
std::string s="Hello☺";
// at this point s contains a line of text
// which may be ANSI or UTF-8 encoded
// convert std::string to ICU's UnicodeString
icu::UnicodeString ucs = icu::UnicodeString::fromUTF8(icu::StringPiece(s.c_str()));
// convert UnicodeString to std::wstring
std::wstring ws;
for (int i = 0; i < ucs.length(); ++i)
ws += static_cast<wchar_t>(ucs[i]);
std::wcout << ws << std::endl;
}
Ожидаемый результат:
Hello☺
Фактический выход:
Hello?
Подскажите, пожалуйста, что я делаю не так. Также предложите любые альтернативные / более простые подходы
Спасибо
Обновление 1 (старое): рабочий код выглядит следующим образом:
#include <iostream>
#include <string>
#include <locale>
#include "unicode/unistr.h"
void f(const std::string & s)
{
std::wcout << "Inside called function" << std::endl;
constexpr char locale_name[] = "";
setlocale( LC_ALL, locale_name );
std::locale::global(std::locale(locale_name));
std::ios_base::sync_with_stdio(false);
std::wcin.imbue(std::locale());
std::wcout.imbue(std::locale());
// at this point s contains a line of text which may be ANSI or UTF-8 encoded
// convert std::string to ICU's UnicodeString
icu::UnicodeString ucs = icu::UnicodeString::fromUTF8(icu::StringPiece(s.c_str()));
// convert UnicodeString to std::wstring
std::wstring ws;
for (int i = 0; i < ucs.length(); ++i)
ws += static_cast<wchar_t>(ucs[i]);
std::wcout << ws << std::endl;
}
int main()
{
constexpr char locale_name[] = "";
setlocale( LC_ALL, locale_name );
std::locale::global(std::locale(locale_name));
std::ios_base::sync_with_stdio(false);
std::wcin.imbue(std::locale());
std::wcout.imbue(std::locale());
std::wcout << "Inside main function" << std::endl;
std::string s=u8"hello☺";
// at this point s contains a line of text which may be ANSI or UTF-8 encoded
// convert std::string to ICU's UnicodeString
icu::UnicodeString ucs = icu::UnicodeString::fromUTF8(icu::StringPiece(s.c_str()));
// convert UnicodeString to std::wstring
std::wstring ws;
for (int i = 0; i < ucs.length(); ++i)
ws += static_cast<wchar_t>(ucs[i]);
std::wcout << ws << std::endl;
std::wcout << "--------------------------------" << std::endl;
f(s);
return 0;
}
Теперь ожидаемый и фактический результат одинаковы, то есть:
Inside main function
hello☺
--------------------------------
Inside called function
hello☺
Обновление 2 (последнее): код, упомянутый в обновлении 1, не работает для символов UTF32, например. Итак, рабочий код для всех возможных символов Unicode выглядит следующим образом. Особая благодарность @Botje за его решение. Хотел бы я дать его решению больше одной галочки!!!:)
#include <iostream>
#include <string>
#include <locale>
#include "unicode/unistr.h"
#include "unicode/ustream.h"
void f(const std::u32string & s)
{
std::wcout << "INSIDE CALLED FUNCTION:" << std::endl;
icu::UnicodeString ustr = icu::UnicodeString::fromUTF32(reinterpret_cast<const UChar32 *>(s.c_str()), s.size());
std::cout << "Unicode string is: " << ustr << std::endl;
std::cout << "Size of unicode string = " << ustr.countChar32() << std::endl;
std::cout << "Individual characters of the string are:" << std::endl;
for(int i=0; i < ustr.countChar32(); i++)
std::cout << icu::UnicodeString(ustr.char32At(i)) << std::endl;
std::cout << "--------------------------------" << std::endl;
}
int main()
{
std::cout << "--------------------------------" << std::endl;
constexpr char locale_name[] = "";
setlocale( LC_ALL, locale_name );
std::locale::global(std::locale(locale_name));
std::ios_base::sync_with_stdio(false);
std::wcin.imbue(std::locale());
std::wcout.imbue(std::locale());
std::wcout << "INSIDE MAIN FUNCTION:" << std::endl;
std::u32string s=U"hello☺";
icu::UnicodeString ustr = icu::UnicodeString::fromUTF32(reinterpret_cast<const UChar32 *>(s.c_str()), s.size());
std::cout << "Unicode string is: " << ustr << std::endl;
std::cout << "Size of unicode string = " << ustr.countChar32() << std::endl;
std::cout << "Individual characters of the string are:" << std::endl;
for(int i=0; i < ustr.countChar32(); i++)
std::cout << icu::UnicodeString(ustr.char32At(i)) << std::endl;
std::cout << "--------------------------------" << std::endl;
f(s);
return 0;
}
Теперь ожидаемый и фактический результат одинаковы, то есть:
--------------------------------
INSIDE MAIN FUNCTION:
Unicode string is: hello☺
Size of unicode string = 7
Individual characters of the string are:
h
e
l
l
o
☺
--------------------------------
INSIDE CALLED FUNCTION:
Unicode string is: hello☺
Size of unicode string = 7
Individual characters of the string are:
h
e
l
l
o
☺
--------------------------------
1 ответ
Есть несколько камней преткновения, чтобы понять это правильно:
- Во-первых, ваш файл (и смайлик в нем) должен быть закодирован как UTF-8. Смайлик должен состоять из буквальных байтов.
0xE2 0x98 0xBA
. - Вы должны пометить строку как содержащую данные UTF-8, используя
u8
декоратор:u8"Hello☺"
- Далее документация
icu::UnicodeString
отмечает, что он хранит Unicode как UTF-16. В этом случае вам повезло, поскольку U+263A умещается в одном символе UTF-16. Другие смайлики могут и не быть! Вы должны либо преобразовать его в UTF-32, либо быть очень осторожным и использоватьGetChar32At
функция. - Наконец, кодировка, используемая
wcout
должен быть настроен сimbue
чтобы соответствовать кодировке, ожидаемой вашей средой. Смотрите ответы на этот вопрос.