2016-10-24 6 views
1

Например, предположим, что переменная strings является ячейка, содержащая строки, например:Как отфильтровать элементы, которые не соответствуют регулярному выражению?

strings = {'alpha' 'basis' 'colic' 'druid' 'even' 'fluff' 'golf'}; 

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

{'alpha' 'colic' 'druid' 'fluff'} 

В целом,

Я хочу, чтобы фильтровать массив ячеек строк, чтобы удалить все строки, которые не соответствуют некоторому регулярному выражению.

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

~~cellfun(@numel, regexp(strings, '^(.).*\1$')) 

IOW,

>> strings(~~cellfun(@numel, regexp(strings, '^(.).*\1$'))) 
ans = 
    'alpha' 'colic' 'druid' 'fluff' 

Но ~~cellfun(@numel, regexp(strings, '^(.).*\1$')) является нечитаемым уродство.

Есть ли более четкий способ фильтрации массива ячеек, чтобы сохранить соответствия регулярному выражению?


EDIT: на основе ответа excaza, я определил следующие функции:

% grep.m 
function filtered = grep(pattern, cellarray) 
%GREP find matches to PATTERN in a cell array of strings. 
%  GREP(PATTERN, CELLARRAY) returns a cell array 
%  containing all the strings in CELLARRAY that match the 
%  regular expression PATTERN. CELLARRAY is expected to 
%  be a cell array of strings. 

    filtered = cellarray(matchq(cellarray, pattern)); 
end 

% matchq.m 
function yn = matchq(string, pattern) 
%MATCHQ predicate stating whether STRING matches PATTERN. 
% If STRING is a single string, MATCHQ(STRING, PATTERN) 
% returns a logical value corresponding to whether or not 
% STRING matches pattern. If STRING is a cell array of 
% strings, MATCHQ(STRING, PATTERN) returns a logical vector 
% whose i-th entry equals MATCHQ(STRING{i}, PATTERN). 

    if ischar(string) 
     yn = ~isempty(regexp(reshape(string, 1, []), pattern, 'match')); 
    else 
     assert(iscellstr(string)); 
     yn = cellfun(@(s) matchq(s, pattern), string); 
    end 
end 

С этими определениями,

>> grep('^(.).*\1$', strings) 
ans = 
    'alpha' 'colic' 'druid' 'fluff' 

FWIW, grep еще "работает", даже если strings состоит из условно-символьные векторы характера:

>> grep('^(.).*\1$', {['aus';'tra';'lia'], ['basis']', ['ce';'lt';'ic'], ... 
         ['dia';'led'], 'early', ['foo';'lpr';'oof'], ... 
         ['gyp';'sum']}) 
ans = 
    [3x3 char] [3x2 char] [2x3 char] [3x3 char] 

>> cellfun(@(c) reshape(c', [], 1)', ans, 'UniformOutput', false) 
ans = 
    'australia' 'celtic' 'dialed' 'foolproof' 
+1

Используйте ' '^ ((?!.). * \ 1 $) ''или''^(?! (.). * \ 1 $). * '' –

+0

@ WiktorStribiżew:' regexp ('^(?! (.). * \ 1 $) ', строки) 'оценивается эквивалентом' repmat ({[]}, 1, numel (строки)) '. Тоже для 'regexp ('^ (?! (.). * \ 1 $). *', Строки)'. – kjo

ответ

2

За regexp's documentation Вы можете использовать 'match' output keyword, чтобы запросить возврат текста, соответствующего вашему выражению. regexp работает на клеточных массивах изначально, поэтому нет необходимости называть его cellfun. Однако для обеспечения надежности Qу него есть (часто раздражающий) behavior, возвращающий массив ячеек ячеек, где каждая ячейка соответствует выходу regexp для входной ячейки.

Это приводит к следующему сценарию:

strings = {'alpha' 'basis' 'colic' 'druid' 'even' 'fluff' 'golf'}; 
matches = regexp(strings, '^(.).*\1$', 'match'); 

который возвращает:

matches = 

    1×7 cell array 

    {1×1 cell} {} {1×1 cell} {1×1 cell} {} {1×1 cell} {} 

Чтобы избавиться от пустых ячеек, вы можете использовать основной цикл или cellfun (по существу эквивалентный цикл):

strings = {'alpha' 'basis' 'colic' 'druid' 'even' 'fluff' 'golf'}; 
matches = regexp(strings, '^(.).*\1$', 'match'); 
emptymask = cellfun('isempty', matches); 
matches(emptymask) = []; 

Который возвращает:

matches = 

    1×4 cell array 

    {1×1 cell} {1×1 cell} {1×1 cell} {1×1 cell} 

Вам понадобится еще один шаг, чтобы опознать ячейки.Это можно сделать с помощью простого цикла или с cellfun (по существу эквивалентно петле):

strings = {'alpha' 'basis' 'colic' 'druid' 'even' 'fluff' 'golf'}; 
matches = regexp(strings, '^(.).*\1$', 'match'); 
emptymask = cellfun('isempty', matches); 
matches(emptymask) = []; 
matches = cellfun(@(x) x{:}, matches, 'UniformOutput', false); 

Который возвращает:

matches = 

    1×4 cell array 

    'alpha' 'colic' 'druid' 'fluff' 

Если можно предположить, что там должен быть только один матч на ячейку вашего массива входных ячеек (или строки), то вы можете использовать 'once' search option для устранения одного слоя:

strings = {'alpha' 'basis' 'colic' 'druid' 'even' 'fluff' 'golf'}; 
matches = regexp(strings, '^(.).*\1$', 'match', 'once'); 

Который возвращает:

matches = 

    1×7 cell array 

    'alpha' '' 'colic' 'druid' '' 'fluff' '' 

Это может быть пропущен через ту же маску, как наивный подход:

strings = {'alpha' 'basis' 'colic' 'druid' 'even' 'fluff' 'golf'}; 
matches = regexp(strings, '^(.).*\1$', 'match', 'once'); 
emptymask = cellfun('isempty', matches); 
matches(emptymask) = []; 

Который возвращает:

matches = 

    1×4 cell array 

    'alpha' 'colic' 'druid' 'fluff' 
+0

Спасибо. Основываясь на вашем сообщении, я написал несколько подпрограмм, чтобы заполнить пробел в встроенной библиотеке MATLAB. См. EDIT для моего сообщения. (Для MathWorks: воспользуйтесь этими функциями!) – kjo