Доступ к строкам Юникода MATLAB из C

Как я могу получить доступ к базовым данным Unicode строк MATLAB через интерфейсы MATLAB Engine или MEX C?

Вот пример. Давайте поместим символы Юникода в файл test.txt в кодировке UTF-8, а затем прочитаем его как

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

в МАТЛАБ.

Вот если я сначала делаю feature('DefaultCharacterSet', 'UTF-8'), потом из C engEvalString(ep, "s"), то на выходе получаю обратно текст из файла в виде UTF-8. Это доказывает, что MATLAB хранит его как Unicode внутри. Однако, если я выполню mxArrayToString(engGetVariable(ep, "s")), я получу то, что unicode2native(s, 'Latin-1') дало бы мне в MATLAB: все символы, отличные от Latin-1, заменены кодом символа 26. Что мне нужно, так это получить доступ к базовым данным Unicode в виде строки C в любом формате Unicode (UTF -8, UTF-16 и т. д.) и сохраняя символы, отличные от Latin-1. Возможно ли это?

Моя платформа — OS X, MATLAB R2012b.

Приложение: документация прямо указывает, что "[mxArrayToString()] поддерживает многобайтовые закодированные символы», но это по-прежнему дает мне только приближение Latin-1 к исходным данным.


person Szabolcs    schedule 18.02.2013    source источник
comment
Ссылка на версию MATLAB Answers   -  person Szabolcs    schedule 18.02.2013


Ответы (1)


Во-первых, позвольте мне поделиться несколькими ссылками, которые я нашел в Интернете:

  • #P2#
    #P3#
    #P4# #P5#
    #P6#
  • На странице mxArrayToString указано, что она обрабатывает многобайтовые закодированные символы (unlinke < a href="http://www.mathworks.com/help/matlab/apiref/mxgetstring.html" rel="nofollow noreferrer">mxGetString, который обрабатывает только однобайтовые схемы кодирования). К сожалению, нет примера того, как это сделать.

  • Наконец, вот тема в группе новостей MATLAB, в которой упоминается пара недокументированных связанные с этим функции (вы можете найти их самостоятельно, загрузив библиотеку libmx.dll в такой инструмент, как Dependency Walker в Windows).


Вот небольшой эксперимент, который я провел в 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!
}

Я создаю три строки в коде C, закодированном с помощью ASCII, UTF-8 и UTF-16LE соответственно. Затем я передаю их в MATLAB, используя функцию mxCreateString MEX (и другие ее недокументированные версии).

Я получил последовательности байтов на веб-сайте 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À水

Я использую встроенную возможность 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 ...

Все тесты проводились на WinXP под управлением R2012b с кодировкой по умолчанию:

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

Надеюсь это поможет..


РЕДАКТИРОВАТЬ:

В MATLAB R2014a многие недокументированные функции C были удалены из библиотеки libmx (включая использованные выше) и заменены эквивалентными функциями C++, представленными в пространстве имен matrix::detail::noninlined::mx_array_api.

Вышеприведенные примеры легко настроить (как описано здесь ) для работы на последней версии R2014a.

person Amro    schedule 19.02.2013