3

如何通过 MATLAB 引擎或 MEX C 接口访问 MATLAB 字符串的底层 unicode 数据?

这是一个例子。让我们将 unicode 字符放入一个 UTF-8 编码的文件 test.txt 中,然后将其读取为

fid=fopen('test.txt','r','l','UTF-8');
s=fscanf(fid, '%s')

在 MATLAB 中。

现在,如果我先这样做feature('DefaultCharacterSet', 'UTF-8'),然后从 C 开始engEvalString(ep, "s"),然后作为输出,我会从文件中以 UTF-8 格式取回文本。这证明 MATLAB 在内部将其存储为 unicode。但是,如果我这样做mxArrayToString(engGetVariable(ep, "s")),我unicode2native(s, 'Latin-1')会在 MATLAB 中得到什么:所有非拉丁 1 字符都替换为字符代码 26。我需要以任何 unicode 格式(UTF-8)作为 C 字符串访问底层 unicode 数据、UTF-16 等),并保留非拉丁 1 字符。这可能吗?

我的平台是 OS X,MATLAB R2012b。

附录:文档明确指出“[mxArrayToString()] 支持多字节编码字符”,但它仍然只给了我原始数据的 Latin-1 近似值。

4

1 回答 1

7

首先,让我分享一些我在网上找到的参考资料:

  • 根据mxChar描述,

    MATLAB 在具有多字节字符集的机器上将字符存储为 2 字节 Unicode 字符

    仍然 MBCS 这个词对我来说有点模棱两可,我认为它们在这种情况下是指 UTF-16(尽管我不确定surrogate pairs,这可能使它改为 UCS-2)。

    更新: MathWorks 将措辞更改为:

    MATLAB 对 Unicode 字符使用 16 位无符号整数字符编码。

  • mxArrayToString页面声明它确实处理多字节编码字符(unlinkemxGetString仅处理单字节编码方案)。不幸的是,没有关于如何做到这一点的例子。

  • 最后,这是 MATLAB 新闻组上的一个线程,其中提到了一些与此相关的未记录函数(您可以通过将libmx.dll库加载到Windows 上的Dependency Walker等工具中自己找到这些函数)。


这是我在 MEX 中做的一个小实验:

my_func.c

#include "mex.h"

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
    char str_ascii[] = {0x41, 0x6D, 0x72, 0x6F, 0x00};   // {'A','m','r','o',0}
    char str_utf8[] = {
        0x41,                   // U+0041
        0xC3, 0x80,             // U+00C0
        0xE6, 0xB0, 0xB4,       // U+6C34
        0x00
    };
    char str_utf16_le[] = {
        0x41, 0x00,             // U+0041
        0xC0, 0x00,             // U+00C0
        0x34, 0x6C,             // U+6C34
        0x00, 0x00
    };

    plhs[0] = mxCreateString(str_ascii);
    plhs[1] = mxCreateString_UTF8(str_utf8);        // undocumented!
    plhs[2] = mxCreateString_UTF16(str_utf16_le);   // undocumented!
}

我分别用 ASCII、UTF-8 和 UTF-16LE 编码的 C 代码创建了三个字符串。mxCreateString然后我使用MEX 函数(以及它的其他未记录版本)将它们传递给 MATLAB 。

我通过咨询Fileformat.info网站获得了字节序列: A (U+0041)À (U+00C0)水 (U+6C34)

让我们在 MATLAB 中测试上述函数:

%# call the MEX function
[str_ascii, str_utf8, str_utf16_le] = my_func()

%# MATLAB exposes the two strings in a decoded form (Unicode code points)
double(str_utf8)       %# decimal form: [65, 192, 27700]
assert(isequal(str_utf8, str_utf16_le))

%# convert them to bytes (in HEX)
b1 = unicode2native(str_utf8, 'UTF-8')
b2 = unicode2native(str_utf16_le, 'UTF-16')
cellstr(dec2hex(b1))'  %# {'41','C3','80','E6','B0','B4'}
cellstr(dec2hex(b2))'  %# {'FF','FE','41','00','C0','00','34','6C'}
                       %# (note that first two bytes are BOM markers)

%# show string
view_unicode_string(str_utf8)

unicode_string AÀ水"></p> <p>我正在使用 <a href=用于查看字符串的嵌入式 Java 功能:

function view_unicode_string(str)
    %# create Swing JLabel
    jlabel = javaObjectEDT('javax.swing.JLabel', str);
    font = java.awt.Font('Arial Unicode MS', java.awt.Font.PLAIN, 72);
    jlabel.setFont(font);
    jlabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);

    %# place Java component inside a MATLAB figure
    hfig = figure('Menubar','none');
    [~,jlabelHG] = javacomponent(jlabel, [], hfig);
    set(jlabelHG, 'Units','normalized', 'Position',[0 0 1 1])
end

现在让我们以相反的方向工作(接受来自 MATLAB 的字符串到 C 中):

my_func_reverse.c

#include "mex.h"

void print_hex(const unsigned char* s, size_t len)
{
    size_t i;
    for(i=0; i<len; ++i) {
        mexPrintf("0x%02X ", s[i] & 0xFF);
    }
    mexPrintf("0x00\n");
}

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
    char *str;
    if (nrhs<1 || !mxIsChar(prhs[0])) {
        mexErrMsgIdAndTxt("mex:error", "Expecting a string");
    }
    str = mxArrayToString_UTF8(prhs[0]); // get UTF-8 encoded string from Unicode
    print_hex(str, strlen(str));         // print bytes
    plhs[0] = mxCreateString_UTF8(str);  // create Unicode string from UTF-8
    mxFree(str);
}

我们在 MATLAB 内部对此进行了测试:

>> s = char(hex2dec(['0041';'00C0';'6C34'])');   %# "\u0041\u00C0\u6C34"
>> ss = my_func_reverse(s);
0x41 0xC3 0x80 0xE6 0xB0 0xB4 0x00               %# UTF-8 encoding
>> assert(isequal(s,ss))

最后我应该说,如果由于某种原因您仍然遇到问题,最简单的方法是将非 ASCII 字符串转换为uint8数据类型,然后再将其从 MATLAB 传递到您的引擎程序。

所以在 MATLAB 进程中执行以下操作:

%# read contents of a UTF-8 file
fid = fopen('test.txt', 'rb', 'native', 'UTF-8');
str = fread(fid, '*char')';
fclose(fid);
str_bytes = unicode2native(str,'UTF-8');  %# convert to bytes

%# or simply read the file contents as bytes to begin with
%fid = fopen('test.txt', 'rb');
%str_bytes = fread(fid, '*uint8')';
%fclose(fid);

并使用 Engine API 访问变量,如下所示:

mxArray *arr = engGetVariable(ep, "str_bytes");
uint8_T *bytes = (uint8_T*) mxGetData(arr);
// now you decode this utf-8 string on your end ...

所有测试均在运行 R2012b 且使用默认字符集的 WinXP 上完成:

>> feature('DefaultCharacterSet')
ans =
windows-1252

希望这可以帮助..


编辑:

在 MATLAB R2014a 中,从库中删除了许多未记录libmx的 C 函数(包括上面使用的函数),并替换为在 namespace 下公开的等效 C++ 函数matrix::detail::noninlined::mx_array_api

应该很容易调整上面的示例(如此所述)以在最新的 R2014a 版本上运行。

于 2013-02-19T16:31:07.590 回答