c++ - 如何将原始 MBCS 字符串 (SHIFT-JIS) 从 Windows 转换为 Linux 上的 UTF-8

标签 c++ linux internationalization widestring

我正在 Linux 上编写一个程序,它必须与现有的 Windows 程序交互。我无法修改 windows 程序的工作方式,但我必须与现有数据集成。该程序将通过 TCP 网络套接字接收原始数据结构。不幸的是,windows 程序在数据结构中嵌入了原始的多字节字符串,并且不指示正在使用的代码页。这对英语有效,但对非拉丁语系语言(即:日语)就惨遭失败。充其量,我可以猜测窗口正在使用的代码页。如果我正在运行并且我的语言环境设置为“ja”或“ja_JP”,我将不得不假设 Windows 机器正在使用“SHIFT-JS”代码页……丑陋,但这就是生活。

问题:

假设我在代码页上猜对了,我怎样才能将这些原始 MBCS 字符串转换为 UTF-8 字符串?

这是原始数据的示例:

发送的字符串是:私のクラスへようこそ

从 windows (JP) 接收到的 MBCS 数据是(以字节为单位,添加了额外的“0x00”以确保空终止):

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 段错误(核心转储)。只使用了几次 iconv,我相信代码片段(从在线示例中复制)是正确的,并且似乎可以使用类似的设置但不同(即:德语/法语)来自 Windows 服务器的 mbcs 字符串与基于拉丁语的语言一起使用.

codecvt 函数 std::wstring_convert 似乎尚未在 linux 中实现,即使在使用 -std=c++11 进行编译时也是如此,因此这似乎不是一个选项。

在此先感谢您提供的任何帮助。

-- 编辑--

在“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");

关于c++ - 如何将原始 MBCS 字符串 (SHIFT-JIS) 从 Windows 转换为 Linux 上的 UTF-8,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24661556/

相关文章:

c++ - 使用显式构造函数返回不可复制的不可移动对象

c++ - 在 Cloud9 上安装 AWS C++ SDK 时出现问题

linux - 虚拟机: virtual port serial between 2 vm

.net - 在 Umbraco 中创建多语言站点

angular - 在运行时为 AOT 编译的 Angular 应用程序获取 --locale 的值

c++ - gp_camera_file_get 是如何工作的?

c++ - Makefile.am 中的 ifdef

linux - 如果父文件夹与 bash 同名,则移动子文件夹的内容

在docker中执行命令的Python文件

javascript - 使用 Nuxt Js i18n 在头部添加 RTL 条件