2013-06-28 3 views
8

Для читателей, не относящихся к MATLAB: не уверен, в какую семью они принадлежат, но регулярные выражения MATLAB описаны полностью here. Характер комментария MATLAB: % (процент), а его разделитель строк - ' (апостроф). Строковый разделитель внутри строки записывается как двойной апостоф ('this is how you write "it''s" in a string.'). Чтобы усложнить дело, операторы транспонирования матриц равны также апострофы (A' (эрмиты) или A.' (обычные)).Как удалить завершающие комментарии через regexp?

Теперь, по темным причинам (я буду не уточнить :): Я пытаюсь интерпретировать код MATLAB на собственном языке MATLAB.

В настоящее время я пытаюсь удалить все завершающие комментарии в массиве ячеек строк, каждый из которых содержит строку кода MATLAB. На первый взгляд это может показаться простым:

>> str = 'simpleCommand(); % simple trailing comment'; 
>> regexprep(str, '%.*$', '') 
ans = 
    simpleCommand(); 

Но, конечно же, что-то подобное может прийти:

>> str = ' fprintf(''%d%*c%3.0f\n'', value, args{:}); % Let''s do this! '; 
>> regexprep(str, '%.*$', '') 
ans = 
    fprintf('  %// <-- WRONG! 

Очевидно, что нам нужно исключить все символы комментария, которые находятся внутри строки из матча , а также принимая во внимание, что один апостроф (или dot-aposrotphe), непосредственно следуя за оператором, является оператором , а не разделителем строк.

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

>> str = { 
     'myFun({''test'' ''%''}); % let''s '     
     'sprintf(str, ''%*8.0f%*s%c%3d\n''); % it''s '   
     'sprintf(str, ''%*8.0f%*s%c%3d\n''); % let''s '  
     'sprintf(str, ''%*8.0f%*s%c%3d\n''); ' 
     'A = A.'';%tight trailing comment' 
    }; 
>> 
>> C = regexprep(str, '(^.*)([email protected](sum(\1==''''''''),2)==0;)(%.*$)', '$1') 

Однако

C = 
    'myFun({'test' '%'}); '    %// sucess 
    'sprintf(str, '%*8.0f%*s%c%3d\n'); ' %// sucess 
    'sprintf(str, '%*8.0f%*s%c%3d\n'); ' %// sucess 
    'sprintf(str, '%*8.0f%*s%c'   %// FAIL 
    'A = A.';'       %// success (although I'm not sure why) 

так что я почти там, но еще не совсем :)

К сожалению, я исчерпал количество времени, которое могу потратить на размышления об этом, и вам нужно продолжить с другими вещами, поэтому, возможно, кто-то, у кого больше времени, достаточно дружелюбен, чтобы думать о эти вопросы:

  1. является комментарий символов внутри строку исключения в только мне нужно остерегаться?
  2. Каков правильный и/или более эффективный способ сделать это?
+0

Я не знаю, что такое matlab, но как насчет использования не жадного квантификатора: '%. *? $' – Toto

+0

@ M42: ну, похоже, это исправляет проблему, по крайней мере для мой маленький подмножество тестов ... Можете ли вы опубликовать ответ? –

+0

@ M42: не ждать, я не выглядел достаточно близко - это * не * исправляет проблему ... на самом деле, она больше ничего не делает :) –

ответ

4

Это соответствует сопряженное транспонирование случай, проверяя, какие символы разрешены до того один

  1. Числа 2'
  2. Письма A'
  3. Dot A.'
  4. Левая скобка, фигурная скобка и кронштейн A(1)', A{1}' и [1 2 3]'

Это единственные случаи, о которых я могу думать сейчас.

C = regexprep(str, '^(([^'']*''[^'']*''|[^'']*[\.a-zA-Z0-9\)\}\]]''[^'']*)*[^'']*)%.*$', '$1') 

на вашем примере мы возвращает

>> C = regexprep(str, '^(([^'']*''[^'']*''|[^'']*[\.a-zA-Z0-9\)\}\]]''[^'']*)*[^'']*)%.*$', '$1') 

C = 

    'myFun({'test' '%'}); ' 
    'sprintf(str, '%*8.0f%*s%c%3d\n'); ' 
    'sprintf(str, '%*8.0f%*s%c%3d\n'); ' 
    'sprintf(str, '%*8.0f%*s%c%3d\n'); ' 
    'A = A.';' 
+0

+1 (снова): начните хорошо выглядеть ... требуется дальнейшее тестирование. –

+0

@ M42 Ваш комментарий лишний, так как он использует '[^ ''] *'. – Oleg

+0

@ M42 Это не тот случай. Вы видите '[^ '']', но это не настоящая строка. Matlab преобразует его в '[^ ']'. Если вы хотите иметь апостроф в строке MATLAB, вам нужно его повторить. Если вы запустите 'disp ('It' is a string ')', вы получите 'It's string'. –

1

Как о том, что все апостроф перед комментарием приходят парами, как это:

>> str = { 
     'myFun({''test'' ''%''}); % let''s '     
     'sprintf(str, ''%*8.0f%*s%c%3d\n''); % it''s '   
     'sprintf(str, ''%*8.0f%*s%c%3d\n''); % let''s '  
     'sprintf(str, ''%*8.0f%*s%c%3d\n''); ' 
    }; 

>> C = regexprep(str, '^(([^'']*''[^'']*'')*[^'']*)%.*$', '$1') 

C = 
    myFun({'test' '%'}); 
    sprintf(str, '%*8.0f%*s%c%3d\n'); 
    sprintf(str, '%*8.0f%*s%c%3d\n'); 
    sprintf(str, '%*8.0f%*s%c%3d\n'); 
+0

выглядит лучше ... но, к сожалению, не получается что-то вроде' str = 'A = A.' ';% trailing comment' ' –

2

Я предпочитаю злоупотреблять checkcode (замена для старого mlint), чтобы сделать синтаксический анализ. Вот предложение

function strNC = removeComments(str) 
if iscell(str) 
    strNC = cellfun(@removeComments, str, 'UniformOutput', false); 
elseif regexp(str, '%', 'once') 
    err = getCheckCodeId(str); 
    strNC = regexprep(str, '%[^%]*$', ''); 
    errNC = getCheckCodeId(strNC); 
    if strcmp(err, errNC), 
     strNC = removeComments(strNC); 
    else 
     strNC = str; 
    end 
else 
    strNC = str; 
end 
end 

function errid = getCheckCodeId(line) 
fName = 'someTempFileName.m'; 
fh = fopen(fName, 'w'); 
fprintf(fh, '%s\n', line); 
fclose(fh); 
if exist('checkcode') 
    structRep = checkcode(fName, '-id'); 
else 
    structRep = mlint(fName, '-id'); 
end 
delete(fName); 
if isempty(structRep) 
    errid = ''; 
else 
    errid = structRep.id; 
end 
end 

Для каждой строки, он проверяет, если ввести ошибку, урезая линию от последнего % до конца строки.

Для примера он возвращает:

>> removeComments(str) 

ans = 

    'myFun({'test' '%'}); ' 
    'sprintf(str, '%*8.0f%*s%c%3d\n'); ' 
    'sprintf(str, '%*8.0f%*s%c%3d\n'); ' 
    'sprintf(str, '%*8.0f%*s%c%3d\n'); ' 
    'A = A.';' 

Он не удаляет директиву подавления, %#ok, так что вы получите:

>> removeComments('a=1; %#ok') 

ans = 

a=1; %#ok 

Который, вероятно, это хорошая вещь.

+0

+1: ты, мой добрый сэр, быстро становишься моим новым героем! Вы так полны грязных трюков: p В любом случае, хотя я согласен, что это, вероятно, более надежный и «лучший» способ, он чувствует себя ужасно подробным ... плюс, он создает зависимость от версии: я на R2010a , поэтому мне придется использовать 'mlint' вместо' codecheck' (который, в идеале, мне нужно будет проверить). Таким образом, я оставлю вопрос открытым дольше, посмотрим, возможно ли однострочное повторное выражение * действительно * невозможно. –

+0

Thansk за добрые слова! Действительно не так лаконично, как однострочный 'regexprep'. Мне также интересно узнать о решении с регулярным выражением, но я вижу, что многие специальные случаи рассматриваются в основном из-за того, что '' 'можно использовать как для транспонирования string, так и для сопряжения. –

+0

Да, этот апостроф - противный, не так ли? –

4

Посмотрите, что я нашел! :)

The comment stripping toolbox, автор Peter J. Acklam.

Для м-кода, он содержит следующее регулярное выражение:

mainregex = [ ... 
    ' (     ' ... % Grouping parenthesis (content goes to $1). 
    ' (^ | \n)  ' ... % Beginning of string or beginning of line. 
    ' (    ' ... % Non-capturing grouping parenthesis. 
    '      ' ... 
    '' ... % Match anything that is neither a comment nor a string... 
    '  (   ' ... % Non-capturing grouping parenthesis. 
    '   [\]\)}\w.]' ... % Either a character followed by 
    '   ''+  ' ... % one or more transpose operators 
    '   |   ' ... % or else 
    '   [^''%] ' ... % any character except single quote (which 
    '      ' ... % starts a string) or a percent sign (which 
    '      ' ... % starts a comment). 
    '  )+   ' ... % Match one or more times. 
    '      ' ... 
    '' ... % ...or... 
    '  |    ' ... 
    '      ' ... 
    '' ... % ...match a string. 
    '  ''   ' ... % Opening single quote that starts the string. 
    '   [^''\n]* ' ... % Zero or more chars that are neither single 
    '      ' ... % quotes (special) nor newlines (illegal). 
    '   (   ' ... % Non-capturing grouping parenthesis. 
    '   ''''  ' ... % An embedded (literal) single quote character. 
    '   [^''\n]* ' ... % Again, zero or more chars that are neither 
    '      ' ... % single quotes nor newlines. 
    '  )*   ' ... % Match zero or more times. 
    '  ''   ' ... % Closing single quote that ends the string. 
    '      ' ... 
    ' )*    ' ... % Match zero or more times. 
    ')     ' ... 
    ' [^\n]*    ' ... % What remains must be a comment. 
       ]; 

    % Remove all the blanks from the regex. 
    mainregex = mainregex(~isspace(mainregex)); 

который становится

mainregex = '((^|\n)(([\]\)}\w.]''+|[^''%])+|''[^''\n]*(''''[^''\n]*)*'')*)[^\n]*' 

и должны быть использованы в качестве

C = regexprep(str, mainregex, '$1') 

До сих пор она выдержала все мои тесты, поэтому я думаю, что это должно решить мою проблему довольно красиво :)

5

Как вы относитесь к использованию недокументированных функций? Если вы не возражаете, вы можете использовать функцию mtree для анализа кода и удаления комментариев. Нет связанных регулярных выражений и we all know, что мы не должны пытаться анализировать контекстно-свободные грамматики с использованием регулярных выражений.

Эта функция представляет собой полный анализатор кода MATLAB, написанный на чистом M-коде. Насколько я могу судить, это экспериментальная реализация, но она уже используется Mathworks в нескольких местах (это то же самое, что используется MATLAB Cody и Contests для измерения длины кода) и может использоваться для other полезных вещей.

Если вход является cellarray строк, мы делаем:

>> str = {..}; 
>> C = deblank(cellfun(@(s) tree2str(mtree(s)), str, 'UniformOutput',false)) 
C = 
    'myFun({ 'test', '%' });' 
    'sprintf(str, '%*8.0f%*s%c%3d\n');' 
    'sprintf(str, '%*8.0f%*s%c%3d\n');' 
    'sprintf(str, '%*8.0f%*s%c%3d\n');' 
    'A = A.';' 

Если у вас уже есть М-файл, сохраненный на диске, вы можете раздеться комментарии просто:

s = tree2str(mtree('myfile.m', '-file')) 

Если вы хотите, чтобы увидеть комментарии, добавьте: mtree(.., '-comments')

+0

, если быть точным ' mtree' вызывает встроенную функцию 'mtreemex', поэтому ее не является чистой функцией M-кода – Amro