Доступ к Unicode-строкам MATLAB из C
Как я могу получить доступ к базовым данным Unicode строк MATLAB через интерфейсы MATLAB Engine или MEX C?
Вот пример. Давайте поместим символы 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 в виде строки C в любом формате Unicode (UTF-8, UTF-16 и т. д.) и сохранение символов не-Latin-1. Это возможно?
Моя платформа - OS X, MATLAB R2012b.
Приложение: Документация прямо заявляет, что "[mxArrayToString()] поддерживает многобайтовые закодированные символы", но все же дает мне только приближение по латинице 1 к исходным данным.
1 ответ
Во-первых, позвольте мне поделиться несколькими ссылками, которые я нашел в Интернете:
В соответствии с
mxChar
описание,MATLAB сохраняет символы как 2-байтовые символы Unicode на машинах с многобайтовыми наборами символов
Тем не менее, термин MBCS несколько двусмысленен для меня, я думаю, что в данном контексте они имели в виду UTF-16 (хотя я не уверен насчет суррогатных пар, что, скорее всего, делает его UCS-2).
ОБНОВЛЕНИЕ: MathWorks изменил формулировку:
MATLAB использует 16-разрядное целое число без знака для символов Юникода.
mxArrayToString
Страница заявляет, что она обрабатывает многобайтовые закодированные символы (unlinkemxGetString
который обрабатывает только однобайтовые схемы кодирования). К сожалению, нет примеров того, как это сделать.Наконец, в новостной группе 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)
Я использую встроенную возможность 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 Engine как:
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.