Пути Unicode с MATLAB

Учитывая следующий код, который пытается создать 2 папки в текущем пути MATLAB:

%%
u_path1 = native2unicode([107, 97, 116, 111, 95, 111, 117, 116, 111, 117], 'UTF-8');  % 'kato_outou'
u_path2 = native2unicode([233 129 142, 230 184 161, 229 191 156, 231 173 148], 'UTF-8');  % '過渡応答'

mkdir(u_path1);
mkdir(u_path2);

первый вызов mkdir завершается успешно, а второй завершается ошибкой с сообщением об ошибке "Неверный синтаксис имени файла, имени каталога или метки тома". Однако создание папок вручную на панели графического интерфейса «Текущая папка» ([щелчок правой кнопкой]Новая папка[вставить имя]) не вызывает проблем. Такого рода сбои появляются в большинстве низкоуровневых функций ввода-вывода MATLAB (dir, fopen, copyfile, movefile и т. д.), и я хотел бы использовать все эти функции.

Окружающая среда:

  • Win7 Корпоративная (32-разрядная, NTFS)
  • МАТЛАБ R2012a

таким образом, файловая система поддерживает символы Unicode в пути, и MATLAB может хранить настоящие строки Unicode (а не «подделывать» их).

mkdir официальная документация элегантно{1} обходит проблему, заявляя что правильный синтаксис для вызова функции:

mkdir('folderName')

что предполагает, что единственный официально поддерживаемый вызов функции — это тот, который использует строковые литералы в качестве аргумента имени папки, а не строковые переменные. Это также предполагает eval способ, который я тестирую, чтобы проверить, работает ли он, пока пишу этот пост.

Интересно, есть ли способ обойти эти ограничения. Меня интересуют решения, которые:

  • не полагайтесь на недокументированные/неподдерживаемые материалы MATLAB;

  • не вносить общесистемные изменения (например, изменение информации о локали операционной системы);

  • в конечном итоге может полагаться на неродные библиотеки MATLAB, если результирующие дескрипторы/объекты можно преобразовать в собственные объекты MATLAB и манипулировать ими как таковыми;

  • в конечном итоге могут полагаться на манипуляции с путями, которые сделают их пригодными для использования стандартными функциями MATLAB, даже если они специфичны для Windows (например, пути с короткими именами).

Позднее редактирование

Я ищу реализации для следующих функций, которые будут скрывать оригиналы в уже написанном коде:

function  listing = dir(folder);
function  [status,message,messageid] = mkdir(folder1,folder2);
function  [status,message,messageid] = movefile(source,destination,flag);
function  [status,message,messageid] = copyfile(source,destination,flag);
function  [fileID, message] = fopen(filename, permission, machineformat, encoding);
function  status = fclose(fileID);
function  [A, count] = fread(fileID, sizeA, precision, skip, machineformat);
function  count = fwrite(fileID, A, precision, skip, machineformat);
function  status = feof(fileID);
function  status = fseek(fileID, offset, origin);
function  [C,position] = textscan(fileID, varargin);  %'This one is going to be funny'

Не все типы вывода должны быть взаимозаменяемы с исходными функциями MATLAB, однако они должны быть согласованы между вызовами функций (например, fileID между fopen и fclose). Я собираюсь обновить этот список объявлений реализациями, как только получу/напишу их.


{1} для очень расплывчатых значений слова "элегантный".


person Community    schedule 03.08.2015    source источник
comment
Я почти ничего не знаю о Matlab, но эти функции, вероятно, являются тонкими оболочками над функциями ввода-вывода файлов стандартной библиотеки C. Эти интерфейсы явно принимают имена файлов в виде строк байтов, которые Windows преобразует в собственный формат UTF-16 с использованием кодовой страницы ANSI. Для приложения, использующего функции stdio, нет способа получить доступ к файлам, имена которых содержат символы вне этой кодировки (и кодовая страница ANSI никогда не бывает такой полезной, как UTF).   -  person bobince    schedule 03.08.2015
comment
@bobince То, как ведут себя эти функции, я тоже подозреваю. Однако я надеялся, что они обернут скорее системные вызовы (которые полностью поддерживают функции базовой файловой системы), чем стандартные библиотечные функции, которые ограничены их исторической подписью аргумента/типа.   -  person    schedule 03.08.2015


Ответы (2)


Некоторая полезная информация о том, как MATLAB обрабатывает имена файлов (и вообще символы), доступна в комментариях этот недокументированный пост Matlab (особенно те, которые написаны Стивом Эддинсом, работающим в MathWorks). Вкратце:

"MathWorks начала преобразовывать всю обработку строк в кодовой базе MATLAB в UTF-16.... и мы приблизились к этому постепенно"

-- Стив Эддинс, декабрь 2014 г.

Это утверждение подразумевает, что чем новее версия MATLAB, тем больше функций поддерживает UTF-16. Это, в свою очередь, означает, что если существует возможность обновить вашу версию MATLAB, это может быть простым решением вашей проблемы.

Ниже приведен список функций, которые были протестированы пользователями на разных платформах в соответствии с функциональностью, которая была запрошена в вопросе:

  • Следующая команда создает каталог с символами UTF16 в его имени (в этом примере «תיקיה» на иврите) из MATLAB:

    java.io.File(fullfile(pwd,native2unicode(...
              [255 254 234 5 217 5 231 5 217 5 212 5],'UTF-16'))).mkdir();
    

    Протестировано на:

    • Windows 7 with MATLAB R2015a by Dev-iL
    • OSX Yosemite (10.10.4) с MATLAB R2014b от Мэтта
  • Следующие команды также успешно создают каталоги:

    mkdir(native2unicode([255 254 234 5 217 5 231 5 217 5 212 5],'utf-16'));
    mkdir(native2unicode([215,170,215,153,215,167,215,153,215,148],'utf-8'));
    

    Протестировано на:

    • Windows 10 with MATLAB R2015a having feature('DefaultCharacterSet') => windows-1255 by Dev-iL
    • OSX Yosemite (10.10.4) с MATLAB R2014b от Matt
      Значение feature('DefaultCharacterSet') здесь не влияет, поскольку кодировка явно определяется в команде native2unicode.
  • Следующие команды успешно открывают файл, содержащий символы Юникода как в имени, так и в содержимом:

    fid = fopen([native2unicode([255,254,231,5,213,5,209,5,229,5],'utf-16') '.txt']);
    txt =  textscan(fid,'%s');
    

    Протестировано на:

    • Windows 10 with MATLAB R2015a having feature('DefaultCharacterSet') => windows-1255 by Dev-iL. Note: the scanned text appears correctly in the Variables view. The text file can be edited and saved from the MATLAB editor with UTF characters intact.
    • OSX Yosemite (10.10.4) с MATLAB R2014b от Matt
      Если для feature('DefaultCharacterSet') установлено значение utf-8 перед использованием textscan, вывод celldisp(txt) отображается правильно. То же самое относится и к представлению Переменные.
person Community    schedule 09.08.2015
comment
@Matt - На самом деле идея заключалась в том, что это будет список, и любой желающий может добавить дополнительные функции из тех, которые запросил OP. Мой ответ был в начале списка, а позже появятся другие пункты. Поэтому я думаю, что ваше редактирование должно быть в основном отменено. - person Dev-iL; 09.08.2015

Попробуйте использовать UTF-16, если вы работаете в Windows, потому что NTFS использует UTF-16 для кодирования имен файлов, а Windows имеет два набора API: те, которые работают с так называемыми «кодовыми страницами Windows» (1250, 1251, 1252 и т. д.) и используют Тип данных C char и те, которые используют wchar_t. Последний тип имеет размер 2 байта в Windows, что достаточно для хранения единиц кода UTF-16.

Причина, по которой ваш первый вызов сработал, заключается в том, что первые 128 кодовых точек в стандарте Unicode закодированы в UTF-8 идентично 128 символам ASCII (что сделано специально для обратной совместимости). UTF-8 использует 1-байтовые единицы кода (вместо 2-байтовых единиц кода для UTF-16), и обычно программное обеспечение, такое как MATLAB, не обрабатывает имена файлов, поэтому им нужно просто хранить последовательности байтов и передавать их в API ОС. Второй вызов не удался, потому что последовательности байтов UTF-8, представляющие кодовые точки, вероятно, отфильтровываются Windows, поскольку некоторые значения байтов запрещены в именах файлов. В POSIX-совместимых операционных системах большинство API-интерфейсов ориентированы на байты, а стандарт в значительной степени предотвращает вы не должны использовать существующие многобайтовые кодировки в API (например, UTF-16, UTF-32) и должны использовать char* API и кодировки с 1-байтовыми кодовыми единицами:

POSIX.1-2008 устанавливает только следующие требования к закодированным значениям символов в переносимом наборе символов:

...

  • Закодированные значения, связанные и должны быть неизменными для всех языковых стандартов, поддерживаемых реализацией.
  • Каждое закодированное значение, связанное с элементами переносимого набора символов, представлено одним байтом. Более того, если значение хранится в объекте языка C типа char, оно гарантированно будет положительным (кроме NUL, который всегда равен нулю).

Не все POSIX-совместимые операционные системы проверяют имена файлов, кроме точки или косой черты, поэтому вы можете в значительной степени хранить мусор в именах файлов. Mac OS X, как система POSIX, использует байт-ориентированные (char*) API, но лежащий в основе HFS+ использует UTF-16 в NFD (Форма нормализации D) , поэтому некоторая обработка выполняется на уровне ОС перед сохранением имени файла.

Windows не выполняет какую-либо нормализацию Unicode и сохраняет имена файлов в любой форме, в которой они передаются в UTF-16 (при условии, что используется NTFS) или кодовых страницах Windows (не уверен, как они обрабатывают это на уровне файловой системы - возможно, путем преобразования).

Итак, как это связано с MATLAB? Что ж, он кроссплатформенный, и из-за этого приходится решать много проблем. Одна из них заключается в том, что в Windows есть char API для кодовых страниц Windows и некоторых запрещенных символов в именах файлов, а в других ОС их нет. Они могли бы реализовать системно-зависимые проверки, но это было бы намного сложнее тестировать и поддерживать (я полагаю, много взбалтывания кода).

Мой лучший совет — использовать UTF-16 в Windows, внедрить проверки, зависящие от платформы, или использовать ASCII, если вам нужна переносимость.

person Dmitrii S.    schedule 04.08.2015
comment
Спасибо за обширный ответ. Несколько комментариев: 1) вызовы native2unicode производят UTF-16 (или, по крайней мере, UCS-2) из ​​байтов UTF-8. Это легко проверить при преобразовании строки u_path2 в числовую: double(u_path2) дает [36942, 28193, 24540, 31572], как и ожидалось для строки из 4 идеограмм. 2) Я понимаю, почему эти функции могут не работать (хотя для среды программирования, заявляющей о себе на острие инженерной мысли, не реализовать стандарт 90-х - это как-то хромает). Я готов настаивать на расширенном решении, а не на решении ограниченной проблемы. :-) - person ; 05.08.2015
comment
Что насчет: u_path2 = native2unicode([233 129 142, 230 184 161, 229 191 156, 231 173 148], 'UTF-16'); Работает ли это в Windows? - person Dmitrii S.; 05.08.2015
comment
Та же ошибка при попытке создать папку. Однако содержимое строки изменяется (как и ожидалось). - person ; 05.08.2015
comment
@CST-Link, я просмотрел их документы, и они, кажется, даже не предоставляют кодировку UTF-16 в качестве значения для настройки кодировки символов по умолчанию (mathworks.com/help/simulink/slref/slcharacterencoding.html). Вы можете попробовать slCharacterEncoding("UTF-16"), а затем выполнить mkdir, но я предполагаю, что у них есть какая-то жестко закодированная логика, которая преобразует путь из UTF-16 в определенную кодовую страницу Windows в зависимости от локали. Это действительно неубедительно, но я видел много дорогих приложений, которые не смогли должным образом реализовать поддержку стандарта Unicode, так что это неудивительно. - person Dmitrii S.; 05.08.2015
comment
Спасибо за более глубокое изучение этого вопроса. К сожалению, эта функция доступна только для Simulink, которого у меня нет. На самом деле я пытаюсь использовать классы java.io. * для манипулирования файлами. Я очень надеюсь, что сюрпризов там не будет... :-) - person ; 05.08.2015
comment
@ CST-Link, Java использует UTF-16 в памяти, но UTF-8 для исходного кода. Насколько я помню, они сопоставляют свои функции ввода-вывода с соответствующими вызовами функций (то есть wchar_t в Windows), поэтому я предполагаю, что если вы создаете каталоги с использованием функций Java, вы сможете получить действительные результаты. - person Dmitrii S.; 05.08.2015
comment
@CST-Link Следуя последнему предложению Дмитрия, я попробовал способ Java , и у меня работает следующая команда: java.io.File(fullfile(pwd,native2unicode([255 254 234 5 217 5 231 5 217 5 212 5],'UTF-16'))).mkdir(); - person Dev-iL; 05.08.2015
comment
@Dev-iL Dev-iL тоже проверил, и, похоже, работает хорошо, спасибо. Создание папки/запрос решен. :-) Проверка функций файлового ввода/вывода. - person ; 05.08.2015