我正在使用期望utf8编码的std :: string变量的代码 . 我希望能够处理用户提供的文件,该文件可能具有utf-16编码(我不知道设计时的编码,但最终希望能够处理utf8 / 16/32),读取它行-by-line,并将每一行转发给代码的其余部分作为utf8编码的std :: string .
我有c 11(真的,c 11的当前MSVC子集)和1.55.0的增强功能 . 我最终需要代码才能在Linux和Windows上运行 . 目前,我只是在Windows上使用Visual Studio 2013 Update 4进行原型设计,在Windows 7上运行 . 我对其他依赖项持开放态度,但他们需要 Build 一个已 Build 的跨平台(即windows和* nix)轨道记录,不应该是GPL / LGPL .
我一直在假设我似乎无法找到验证的方法,而且我的代码无效 .
一个假设是,由于我最终希望std :: string变量中的这些文件中的每一行,我应该使用std :: ifstream,并使用正确构造的codecvt,以便传入的utf16流可以转换为utf8 .
这个假设是否现实?我想,另一种选择是我必须对文本文件进行一些编码检查,然后根据结果选择wifstream / wstring或ifstream / string,这似乎比我想要开始时没有吸引力 . 当然,如果这是正确的(或唯一的现实的)路径,我愿意接受它 .
我意识到我可能还需要进行一些编码检测,但就目前而言,我并不关心编码检测部分,只关注将utf16文件内容转换为utf8 std :: string .
我尝试过各种不同的locale和codecvt组合,但都没有 . 以下是我认为可行的最新版本,但不是:
void
SomeRandomClass::readUtf16LeFile( const std::string& theFileName )
{
boost::locale::generator gen;
std::ifstream file( theFileName );
auto utf8Locale = gen.generate( "UTF-8" );
std::locale cvtLocale( utf8Locale,
new std::codecvt_utf8_utf16<char>() );
file.imbue( utf8Locale );
std::string line;
std::cout.imbue( utf8Locale );
for ( int i = 0; i < 3; i++ )
{
std::getline( file, line );
std::cout << line << std::endl;
}
}
我在这段代码中看到的行为是,无论文件内容如何,每次调用getline()的结果都是一个空字符串 .
如果省略上述方法的第3行和第5行,这个相同的代码工作正常(意思是,每个getline()调用在同一文件的utf8编码版本上返回正确编码的非空字符串) .
无论出于何种原因,我都无法在SO或者http://en.cppreference.com/或其他任何地方找到任何试图做同样事情的例子 .
所有的想法/建议(符合上述要求)欢迎 .
1 回答
阅读UTF-16编写UTF-8
你必须澄清的第一个问题是你正在阅读的UTF16的变体:
是UTF-16LE(即在Windows下生成)?
是UTF-16BE(默认情况下由wstream生成)?
是带有BOM的UTF16吗?
接下来的问题是要知道你是否真的可以在控制台上输出你的UTF8或UTF16,因为他们知道默认的Windows控制台确实可以引起头部争议 .
Step 1: Make sure that the problem is no related to the win console
所以这里有一个小代码来读取UTF-16LE并使用本机Windows函数检查内容(您只需在控制台应用程序中包含
<windows.h>
):如果您的文件是带有BOM的UTF-16,只需将
litte_endian
替换为consume_header
即可 .Step 2: Convert your utf16-string back into utf8 string
你必须使用字符串转换器:
这将在win控制台上向您显示ascii字符 . 但是,所有utf8编码都将显示为垃圾(除非您在设置控制台以显示unicode字体方面比我更成功) .
Step 3: check the utf8 encoding using a file
由于win控制台非常糟糕,最好的方法是将您直接生成的字符集写入文件,然后使用文本编辑器(lke Notepad)打开此文件,它可以显示编码 .
Nota bene: 所有这些只使用标准库(中间件
MessageBoxW()
除外)及其语言环境完成 .进一步的步骤
如果要检测编码,首先要检查文件的开头是否有BOM(为二进制输入打开,默认为“C”语言环境):
只需加载前几个字节,然后与此数据进行比较 .
如果你找到一个,那没关系 . 如果没有,您将不得不遍历该文件 .
如果你期望西方语言,快速近似如下:如果你发现很多空字节(> 25%<50%),它可能是utf16 . 如果你发现超过50%的空值,它可能是utf32 .
但更精确的方法可能有意义 . 例如,要验证文件是否为UTF16,您只需要实现一个小型状态机,检查第一个字在0xD8和0xDB之间的高字节,下一个字的高字节在0xDC和0xDF之间 . 如果它是小端或大端,那么什么是高和什么低取决于当然 .
对于UTF8来说,这是一种类似的做法,但是状态机有点复杂,因为第一个字符的位模式定义了必须遵循的字符数,并且每个字符必须具有位模式
(c & 0xC0) == 0x80
.