在 Mac 中使用 C++ 读取 UTF-8 数据无效
Reading UTF-8 data with C++ in Mac not working
虽然我的 C++ 经验大大减少,但我正在努力帮助 C++ 程序员让他的库在 Mac 上工作。目前,问题似乎只与 locale/encoding 有关。
尝试创建一个最小的工作示例,我测试了以下代码,它将一行 UTF-8 字符读取为一个宽字符串 (wstring),然后遍历该字符串并打印每个字符。
虽然它在 Linux 框上完美运行,所有字符都打印在不同的行中,但在使用 Mac 框时我得到每个 byte每行打印(而不是每个字符)。
密码是:
#include <sstream>
#include <iostream>
#include <string>
#include <boost/locale.hpp>
using namespace std;
int main() {
std::ios_base::sync_with_stdio(false);
boost::locale::generator gen;
locale mylocale = gen("pt_PT.UTF-8");
locale::global(mylocale);
wstring userInput;
getline(wcin, userInput);
wcerr << "Size of string is " << userInput.length() << endl;
for (int i = 0; i < userInput.length(); ++i) {
wcerr << userInput.at(i) << endl;
}
return 0;
}
我的测试字符串是一个愚蠢的葡萄牙语句子:
O coração é um órgão frágil.
我正在尝试使用 Boost_locale,因为有人告诉我这是让 unicode 在 Mac 上正常工作的方法,但我很乐意有一个仅使用 C++ 标准库的解决方案.
编辑:
以下代码适用于 Mac。由于包含 codecvt,它无法在我的 Linux 机器上编译,但我可以使用一些 CPP 指令来管理它。
#include <sstream>
#include <iostream>
#include <fstream>
#include <codecvt>
#include <locale>
#include <string>
using namespace std;
int main() {
// setting std::local::global seems not to work (??)
wcin.imbue(std::locale(locale(""), new std::codecvt_utf8<wchar_t>));
wcerr.imbue(std::locale(locale(""), new std::codecvt_utf8<wchar_t>));
wstring userInput;
getline(wcin, userInput);
wcerr << "Size of string is " << userInput.length() << endl;
for (int i = 0; i < userInput.length(); ++i) {
wcerr << userInput.at(i) << endl;
}
return 0;
}
此行为是由于在 UTF-8 编码中,字符(也称为 代码点 由一个或多个 代码单元表示.
本质上是:
for (int i = 0; i < userInput.length(); ++i)
循环 代码单元 。您可以通过 userInput.length()
是一个大于字符串中字符数的数字来验证该行为。
通过做:
wcerr << userInput.at(i) << endl;
您在每个 代码单元 后附加一个 endl
,从而将属于同一 [=] 的 代码单元 分开40=]code point 产生无效字符。
如果你只是输出:
wcerr << userInput << endl;
你的字符串会完好无损。
如果你想单独输出每个字符,你将不得不考虑属于同一个代码点的多个代码单元并分别输出。
更新:
默认情况下,wcin
不会转换为 code points。您需要明确说明输入的编码并进行转换。这实质上是以下代码所做的。与您的示例唯一的主要区别是我使用了 C++11 标准库而不是 Boost.
#include <codecvt>
#include <iostream>
int main() {
std::locale::global( std::locale( std::locale(""), new std::codecvt_utf8<wchar_t> ) );
std::wcin.imbue( std::locale() );
std::wcout.imbue( std::locale() );
std::wcerr.imbue( std::locale() );
std::wstring user_input;
std::wcin >> user_input;
for( int i = 0; i < user_input.length(); ++i ) {
std::wcout << user_input[i] << std::endl;
}
// Converting characters to uppercase
const std::ctype<wchar_t>& f = std::use_facet<std::ctype<wchar_t>>( std::locale() );
for( int i = 0; i < user_input.length(); ++i ) {
std::wcout << f.toupper(user_input[i]) << std::endl; // f.tolower() for lowercase
}
return 0;
}
P.S。要编译它,您需要传递 C++11 标准标志。
g++ -std=c++11 main.cpp
虽然我的 C++ 经验大大减少,但我正在努力帮助 C++ 程序员让他的库在 Mac 上工作。目前,问题似乎只与 locale/encoding 有关。
尝试创建一个最小的工作示例,我测试了以下代码,它将一行 UTF-8 字符读取为一个宽字符串 (wstring),然后遍历该字符串并打印每个字符。
虽然它在 Linux 框上完美运行,所有字符都打印在不同的行中,但在使用 Mac 框时我得到每个 byte每行打印(而不是每个字符)。
密码是:
#include <sstream>
#include <iostream>
#include <string>
#include <boost/locale.hpp>
using namespace std;
int main() {
std::ios_base::sync_with_stdio(false);
boost::locale::generator gen;
locale mylocale = gen("pt_PT.UTF-8");
locale::global(mylocale);
wstring userInput;
getline(wcin, userInput);
wcerr << "Size of string is " << userInput.length() << endl;
for (int i = 0; i < userInput.length(); ++i) {
wcerr << userInput.at(i) << endl;
}
return 0;
}
我的测试字符串是一个愚蠢的葡萄牙语句子:
O coração é um órgão frágil.
我正在尝试使用 Boost_locale,因为有人告诉我这是让 unicode 在 Mac 上正常工作的方法,但我很乐意有一个仅使用 C++ 标准库的解决方案.
编辑:
以下代码适用于 Mac。由于包含 codecvt,它无法在我的 Linux 机器上编译,但我可以使用一些 CPP 指令来管理它。
#include <sstream>
#include <iostream>
#include <fstream>
#include <codecvt>
#include <locale>
#include <string>
using namespace std;
int main() {
// setting std::local::global seems not to work (??)
wcin.imbue(std::locale(locale(""), new std::codecvt_utf8<wchar_t>));
wcerr.imbue(std::locale(locale(""), new std::codecvt_utf8<wchar_t>));
wstring userInput;
getline(wcin, userInput);
wcerr << "Size of string is " << userInput.length() << endl;
for (int i = 0; i < userInput.length(); ++i) {
wcerr << userInput.at(i) << endl;
}
return 0;
}
此行为是由于在 UTF-8 编码中,字符(也称为 代码点 由一个或多个 代码单元表示.
本质上是:
for (int i = 0; i < userInput.length(); ++i)
循环 代码单元 。您可以通过 userInput.length()
是一个大于字符串中字符数的数字来验证该行为。
通过做:
wcerr << userInput.at(i) << endl;
您在每个 代码单元 后附加一个 endl
,从而将属于同一 [=] 的 代码单元 分开40=]code point 产生无效字符。
如果你只是输出:
wcerr << userInput << endl;
你的字符串会完好无损。
如果你想单独输出每个字符,你将不得不考虑属于同一个代码点的多个代码单元并分别输出。
更新:
默认情况下,wcin
不会转换为 code points。您需要明确说明输入的编码并进行转换。这实质上是以下代码所做的。与您的示例唯一的主要区别是我使用了 C++11 标准库而不是 Boost.
#include <codecvt>
#include <iostream>
int main() {
std::locale::global( std::locale( std::locale(""), new std::codecvt_utf8<wchar_t> ) );
std::wcin.imbue( std::locale() );
std::wcout.imbue( std::locale() );
std::wcerr.imbue( std::locale() );
std::wstring user_input;
std::wcin >> user_input;
for( int i = 0; i < user_input.length(); ++i ) {
std::wcout << user_input[i] << std::endl;
}
// Converting characters to uppercase
const std::ctype<wchar_t>& f = std::use_facet<std::ctype<wchar_t>>( std::locale() );
for( int i = 0; i < user_input.length(); ++i ) {
std::wcout << f.toupper(user_input[i]) << std::endl; // f.tolower() for lowercase
}
return 0;
}
P.S。要编译它,您需要传递 C++11 标准标志。
g++ -std=c++11 main.cpp