Страницы

Поиск по вопросам

четверг, 25 октября 2018 г.

Регулярное выражение для отбора алфавитных последовательностей без пропусков

Надо написать команду для терминала, используя grep или sed. Она должна выводить только подошедшие куски из текстового файла (без разницы в строку или в столбик). Perl юзать нельзя.
Сейчас есть такое регулярное выржение
a?\s*b?\s*c?\s*d?\s*e?\s*f?\s*g?\s*h?\s*i?\s*j?\s*k?\s*l?\s*m?\s*n?\s*o?\s*p?\s*q?\s*r?\s*s?\s*t?\s*u?\s*v?\s*w?\s*x?\s*y?\s*z?\s*
Как ни странно, под него попадают такие последовательности, как, например, "ace" или "bpxz". Как сделать, чтобы в выражении учитывались только последовательности без пропуска букв, типа "abcd", "opqr", "xy"?
UPD: забыл добавить, что пробелы игнорируются (для этого и испольльзую \s*). Регулярка должна в любом месте текста найти алфавитную последовательность. Например из фразы "перекличка дежурных" должно найтись "кл" и "деж" (на русском проще пример было придумать).


Ответ

Вы к сожалению не указали какой диалект регулярных выражений можно использовать и для чего это надо. Возможно, есть более простые решения, основанные на специальных возможностях регулярных выражений или более простые средства без использования регулярок.
Для PCRE совместимого диалекта получается подобное выражение (до буквы d, продолжать по аналогии, пробелы поставить по вкусу):
(?:a(?=b))?(?:b(?=c))?(?:c(?=d))?(?:d(?=e))?
Тест на ragex101.com
Из "Специальных возможностей" регулярных выражений можно, например, на языке perl проверку любых символов идущих подряд сделать так:
echo "abpade fg xyz" | perl -npe 's/.*?((?:([a-z])\s*(?=(??{chr(ord($2)+1)})))+.)/$1
/g'
Результат: ab de fg xyz
perl вполне можно использовать вместо grep на большинстве unix систем, записав требуемую команду в виде одной строки.
UPD Для командной строки, используя только grep и sed, короткий вариант:
echo "a bcefgkmoxyz" |\ grep -Po `echo -n 'bcdefghijklmnopqrstuvwxyz' |\ sed 's/./\0\0/g;s/^/a/;s/\(.\)\(.\)/\\\\s*(?:\1(?=\\\\s*\2))?/g;s/.$/./'` |\ sed -n '/../p'
Результат: a bc efg xyz
Команда разбита на несколько строк для удобства просмотра, можно в одну строку, убрав \. Мне было лень писать длиннющую регулярку, поэтому в качестве параметра grep передается результат выполнения (в обратных апострофах) команды echo | sed создающей нужное выражение на ходу из букв алфавита. К сожалению идеального выражения не получилось и grep выдает так же отдельные символы, для их подавления используется последняя строка sed -n '/../p'
Параметр grep генерированный командами из алфавита выглядит так:
\\s*(?:a(?=\\s*b))?\\s*(?:b(?=\\s*c))?\\s*(?:c(?=\\s*d))?\\s*(?:d(?=\\s*e))?\\s*(?:e(?=\\s*f))?\\s*(?:f(?=\\s*g))?\\s*(?:g(?=\\s*h))?\\s*(?:h(?=\\s*i))?\\s*(?:i(?=\\s*j))?\\s*(?:j(?=\\s*k))?\\s*(?:k(?=\\s*l))?\\s*(?:l(?=\\s*m))?\\s*(?:m(?=\\s*n))?\\s*(?:n(?=\\s*o))?\\s*(?:o(?=\\s*p))?\\s*(?:p(?=\\s*q))?\\s*(?:q(?=\\s*r))?\\s*(?:r(?=\\s*s))?\\s*(?:s(?=\\s*t))?\\s*(?:t(?=\\s*u))?\\s*(?:u(?=\\s*v))?\\s*(?:v(?=\\s*w))?\\s*(?:w(?=\\s*x))?\\s*(?:x(?=\\s*y))?\\s*(?:y(?=\\s*z))?.

Комментариев нет:

Отправить комментарий