如何将原始MBCS字符串(SHIFT-JIS)从windows转换为linux上的UTF-8
How to convert raw MBCS strings (SHIFT-JIS) from windows to UTF-8 on linux
我正在Linux上编写一个程序,它必须与现有的windows程序接口。我不能修改windows程序的工作方式,但我必须与现有的数据集成。这个程序将通过TCP网络套接字接收原始数据结构。不幸的是,windows程序在数据结构中嵌入原始的多字节字符串,并且不指示正在使用哪个代码页。这对于英语来说是可行的,但是对于非拉丁语系的语言(例如:日语)就失败了。充其量,我可以猜测窗口正在使用的代码页。如果我正在运行并且我的区域设置设置为"ja"或"ja_JP",我将不得不假设windows机器正在使用"SHIFT-JS"代码页…很丑,但这就是生活。
问题:
假设我在代码页上猜对了,我如何将这些原始MBCS字符串转换为UTF-8字符串?
以下是原始数据的示例:
发送的字符串是:
从windows (JP)接收到的MBCS数据是(以字节为单位,添加额外的"0x00"以确保null终止):
char kanji_win_raw_bytes[] = { 0x8E, 0x84, 0x82, 0xCC, 0x83, 0x4E, 0x83, 0x89, 0x83, 0x58, 0x82, 0xD6, 0x82, 0xE6, 0x82, 0xA4, 0x82, 0xB1, 0x82, 0xBB, 0x00, 0x00, 0x00 };
据我所知,该字符串来自使用SHIFT-JS代码页的windows机器。我试过了mbsrtowcs():
const char *ptr = (char*)m_data;
// m_data contains the byte array of MBCS data
if ( m_data != NULL )
{
std::mbstate_t state = std::mbstate_t();
size_t bufflen = std::mbsrtowcs(NULL, &ptr, 0, &state);
if ( bufflen == (size_t)-1 )
{
std::cout << "ERROR! mbsrtowcs() " << strerror(errno) << std::endl;
std::cout << "Error at: " << (int32_t)( (char*)ptr - (char*)m_data ) << std::endl;
return;
}
std::vector<wchar_t> wstr(bufflen);
std::cout << "converting " << bufflen << " characters" << std::endl;
std::mbsrtowcs(&wstr[0], &ptr, wstr.size(), &state);
std::wcout << "Wide string: " << &wstr[0] << std::endl
<< "The length, including '\0': " << wstr.size() << std::endl;
}
对mbsrtowcs()的调用在位置"0"处失败,没有转换字符。
然后我尝试使用SHIFT-JS代码页的iconv库:
bytes_converted = 0;
char input[4096] = {0};
char dst[4096] = {0};
char* src = input;
size_t dstlen = sizeof(dst);
size_t srclen = 0;
iconv_t conv = iconv_open("UTF-8", "SHIFT-JIS" );
// make a copy
memcpy( (void*)input, (void*)kanji_win_raw_bytes, sizeof(kanji_win_raw_bytes) );
srclen = sizeof(kanji_win_raw_bytes);
if ( conv != (iconv_t)-1 )
{
bytes_converted = iconv( conv, NULL, NULL, (char**)&dst, &dstlen );
if ( bytes_converted == (size_t) -1 )
{
std::cerr << "ERROR: initializing output buffer: (" << errno << ") " << strerror(errno) << std::endl;
}
bytes_converted = iconv(conv, (char**)&src, &srclen, (char**)&dst, &dstlen);
if ( bytes_converted == (size_t) - 1)
{
std::cerr << "ERROR in conversion: (" << errno << ") " << strerror(errno) << std::endl;
if ( errno == EINVAL )
{
std::cerr << "RESULT: iconv() converted " << bytes_converted << " bytes: [" << dst << "]" << std::endl;
}
}
else
{
std::cerr << "SUCCESS: iconv() converted " << bytes_converted << " bytes: [" << dst << "]" << std::endl;
}
iconv_close(conv);
}
else
{
std::cerr << "ERROR: iconv_open() failed: " << strerror(errno) << std::endl;
}
Iconv segfaults (coredumps)使用给定的(日文)字符串。我只使用了iconv几次,我相信代码片段(从在线样本复制)是正确的,并且似乎可以使用基于拉丁语的语言使用类似的设置,但不同的(即:德语/法语)mbcs字符串来自windows服务器。
codecvt函数std::wstring_convert似乎尚未在linux中实现,即使在使用-std=c++11编译时也是如此,因此这似乎不是一个选项。
事先感谢您提供的任何帮助。
—Edit—
在"myk"的帮助下,我创建了一个示例应用程序,它更好地显示了我的问题。根据他的建议,我能够绕过分割错误,但是无论我选择的语言环境如何,windows MBCS字符串都无法转换。/**
* MBCS test
*/
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>
#include <clocale>
#include <string>
#include <iostream>
// 私のクラスへようこそ (welcome to my class)
const char* kanji_string = "私のクラスへようこそ";
// This is what raw UTF-8 should look like
uint8_t kanji_utf8_raw_bytes[] = { 0xE7, 0xA7, 0x81, 0xE3, 0x81, 0xAE, 0xE3, 0x82, 0xAF, 0xE3, 0x83, 0xA9, 0xE3, 0x82, 0xB9, 0xE3, 0x81, 0xB8, 0xE3, 0x82, 0x88, 0xE3, 0x81, 0x86, 0xE3, 0x81, 0x93, 0xE3, 0x81, 0x9D };
// This is Windows MBCS using the SHIFT-JS code page
uint8_t kanji_win_raw_bytes[] = { 0x8E, 0x84, 0x82, 0xCC, 0x83, 0x4E, 0x83, 0x89, 0x83, 0x58, 0x82, 0xD6, 0x82, 0xE6, 0x82, 0xA4, 0x82, 0xB1, 0x82, 0xBB, 0x00, 0x00, 0x00 };
int main( int argc, char **argv )
{
std::setlocale(LC_ALL, "en_US.utf8");
std::cout << "KANJI String [" << kanji_string << "]" << std::endl;
std::cout << "KANJI UTF-8 Raw [" << kanji_utf8_raw_bytes << "]" << std::endl;
const char *data = (char*)kanji_win_raw_bytes;
std::mbstate_t state = std::mbstate_t();
size_t result = 0;
wchar_t* buffer = (wchar_t*)malloc( sizeof(wchar_t) * (strlen((char*)data) + 1) );
if ( buffer )
{
result = std::mbsrtowcs(buffer, &data, strlen(data), &state);
if ( result == (size_t)-1 )
{
std::cout << "ERROR! mbsrtowcs() " << strerror(errno) << std::endl;
std::cout << "Error at: " << (int32_t)( (char*)data - (char*)kanji_win_raw_bytes ) << std::endl;
}
else
{
std::wcout << "Wide string: [" << buffer << "] " << std::endl;
}
free( buffer );
}
return 0;
}
注意:这可以在Linux/Mac上编译和运行,使用以下命令:
g++ mbcs_test.cpp -o mbcs_test && ./mbcs_test
对于mbsrtowcs(),需要注意以下几点:
1)调用:
size_t bufflen = std::mbsrtowcs(NULL, &ptr, 0, &state);
应该是这样的:
size_t bufflen = std::mbsrtowcs(buffer, &ptr, strlen(m_data), &state);
假设你用如下方式声明了'buffer':
wchar_t* buffer = (wchar_t*) malloc(sizeof(wchar_t) * (strlen(m_data) + 1));
mbsrtowcs()中的第三个参数,您将其设置为零,是结果缓冲区的长度,这可能是0个字符被转换的原因。
2)我的经验是,你需要使用setlocale()为mbsrtowcs()工作。我无法从代码片段中看到,但建议您包含以下内容:
#include <clocale>
:
std::setlocale(LC_ALL, "en_US.utf8");
- 将 LPHMIXER 转换为 Windows mmeapi 中的 HMIXER
- 将 Genteruse 从 Linux 转换为 Windows
- 绕过 Windows 中的 __declspec(dllimport) 到 Linux 项目的转换
- 如何将Windows产品类型转换为名称?
- 将通用Windows应用程序转换为VisualStudio 2019上的常规应用程序
- 将位图 (bmp) 转换为具有透明度的 png (Windows c++)
- 将带斜杠的 Unix 路径转换为 Windows 路径
- 如何在Windows上将Qt QString转换为LPCTSTR
- 如何将Windows GUID转换为boost::uuid?
- 在C Windows程序中获取A/D转换器示例程序
- 如何将 UTF8 字符数组转换为 Windows 1252 字符数组
- CPP Windows 字符串转换混淆了 CStringA 和 LPCWSTR
- 如何将CString转换为使用WCHAR的Windows API和自定义函数?
- CPP-将源代码从Windows转换为Linux
- Ubuntu上的库ICU不想从Unicode转换为Windows-1251
- 将Windows SYSTEMTIME转换为字符串或字符buf,C++与用户的"Region and Language"格式?
- Windows VC++编译器允许一种奇怪的c样式转换方式(或者它可能不是转换??)
- 如何在 c++ Linux 中使用 ICU 库将 UnicodeString 转换为 windows-1251
- 如何将 utf 字符转换为 windows-1252?
- Windows编程:为什么我们要将lParam转换为CREATESTRUCT来获取应用程序状态