Лабораторная работа № 9 Обработка строк (работа с текстовыми данными)
Автор: student | Категория: Технические науки / Информатика и программирование | Просмотров: 1799 | Комментирии: 0 | 06-03-2014 20:13

Лабораторная работа  № 9

 

Обработка строк (работа с текстовыми данными)

 

Цель работы – практическое знакомство со способами эффективной обработки текста при помощи интерфейса командной строки и набора стандартных утилит

 

1. КРАТКИЕ ТЕОРЕТИЧЕСКИЕ СВЕДЕНИЯ

 

1.1 Ввод и вывод. Перенаправление ввода и вывода

Каждая программа работает с данными определенного типа: текстовыми, графическими, звуковыми и т. п. Основной интерфейс управления системой в Linux - это терминал, который предназначен для передачи текстовой информации от пользователя системе и обратно. Поскольку ввести с терминала и вывести на терминал можно только текстовую информацию, то ввод и вывод программ, связанных с терминалом, тоже должен быть текстовым. Однако необходимость оперировать с текстовыми данными не ограничивает возможности управления системой, а, наоборот, расширяет их. Пользователь может прочитать вывод любой программы и проанализировать, что происходит в системе, а разные программы оказываются совместимыми между собой, поскольку используют один и тот же вид представления данных - текстовый.

"Текстовость" данных - всего лишь договоренность об их формате. Никто не мешает выводить на экран нетекстовый файл, однако пользы в этом будет мало. Во-первых, раз уж файл содержит не текст, то не предполагается, что пользователь сможет что-либо понять из его содержимого. Во-вторых, если в нетекстовых данных, выводимых на терминал, случайно встретится управляющая последовательность, терминал ее выполнит. Например, если в скомпилированной программе записано некоторое число в виде четырех байтов: 27, 91, 49 и 74, соответствующий им текст состоит из четырех символов ASCII: "esc", "[", "1" и "J", и при выводе файла на виртуальную консоль Linux в этом месте выполнится очистка экрана, так как "^[[1J" - именно такая управляющая последовательность для виртуальной консоли. Не все управляющие последовательности столь безобидны, поэтому использовать нетекстовые данные в качестве текстов не рекомендуется.

Если содержимое нетекстового файла все-таки желательно просмотреть (то есть превратить в текст), можно воспользоваться утилитой hexdump с ключом, которая выдает содержимое файла в виде шестнадцатеричных ASCII-кодов, или strings, показывающей только те части файла, которые могут быть представлены в виде текста:

Пример 7.1. Использование hexdump

[student@localhost root]$ hexdump -C /bin/cat | less
  . . .
[student @localhost root]$ strings -n3 /bin/cat | less

В приведенном примере 7.1  утилита hexdump с ключом "-C" выводит в правой стороне экрана текстовое представление данных, заменяя непечатные символы точками (чтобы среди выводимого текста не встретилось управляющей последовательности). Наименьшая длина строки передается strings ключом "-n".

Для того чтобы записать данные в файл или прочитать их оттуда, процессу необходимо сначала открыть этот файл (при открытии на запись, возможно, придется предварительно создать его). При этом процесс получает дескриптор (описатель) открытого файла - уникальное для этого процесса число, которое он и будет использовать во всех операциях записи. Первый открытый файл получит дескриптор 0, второй - 1 и так далее. Закончив работу с файлом, процесс закрывает его, при этом дескриптор освобождается и может быть использован повторно. Если процесс завершается, не закрыв файлы, за него это делает система. Строго говоря, только в операции открытия дескриптора указывается, какой именно файл будет задействован. В качестве "файла" используются и обычные файлы, и устройства (чаще всего - терминалы), и каналы. Дальнейшие операции - чтение, запись и закрытие - работают с дескриптором, как с потоком данных, а куда именно ведет этот поток, неважно.

Каждый процесс Linux получает при старте три "файла", открытых для него системой. Первый из них (дескриптор 0) открыт на чтение, это стандартный ввод процесса. Именно со стандартным вводом работают все операции чтения, если в них не указан дескриптор файла. Второй (дескриптор 1) - открыт на запись, это стандартный вывод процесса. С ним работают все операции записи, если дескриптор файла не указан в них явно. Наконец, третий поток данных (дескриптор 2) предназначается для вывода диагностических сообщений, он называется стандартный вывод ошибок. Поскольку эти три дескриптора уже открыты к моменту запуска процесса, первый файл, открытый самим процессом, будет, скорее всего, иметь дескриптор 3.

Дескриптор - это описатель потока данных, открытого процессом. Дескрипторы нумеруются, начиная с 0. При открытии нового потока данных его дескриптор получает наименьший из неиспользуемых в этот момент номеров. Три заранее открытых дескриптора - стандартный ввод (0), стандартный вывод (1) и стандартный вывод ошибок (2) - выдаются при запуске.

Механизм копирования окружения подразумевает, в числе прочего, копирование всех открытых дескрипторов родительского процесса дочернему. В результате и родительский, и дочерний процесс имеют под одинаковыми дескрипторами одни и те же потоки данных. Когда запускается стартовый командный интерпретатор, все три заранее открытых дескриптора связаны у него с терминалом (точнее, с соответствующим устройством типа tty): пользователь вводит команды с клавиатуры и видит сообщения на экране. Следовательно, любая команда, запускаемая из командной оболочки, будет выводить на тот же терминал, а любая команда, запущенная интерактивно (не в фоне) - вводить оттуда.

Стандартный вывод

Cтандартный вывод (standard output, stdout) - это поток данных, открываемый системой для каждого процесса в момент его запуска и предназначенный для данных, выводимых процессом.

Некоторые утилиты умеют выводить не только на терминал, но и в файл. Например, info при указании ключа "-o" с именем файла выведет текст руководства в файл, вместо того, чтобы отображать его на мониторе. Даже если разработчиками программы не предусмотрен такой ключ, известен и другой способ сохранить вывод программы в файле вместо того, чтобы выводить его на монитор: поставить знак ">" и указать после него имя файла.

Подмена стандартного вывода - задача командной оболочки (shell). Shell создает пустой файл, имя которого указано после знака ">", и дескриптор этого файла передается программе под номером 1 (стандартный вывод). Делается это очень просто. При запуске программы из оболочки после выполнения fork() появляется два одинаковых процесса, один из которых - дочерний - должен запустить вместо себя команду (выполнить exec()). Перед этим он закрывает стандартный вывод (дескриптор 1 освобождается) и открывает файл (с ним связывается первый свободный дескриптор, т. е. 1), а запускаемой команде ничего знать и не надо: ее стандартный вывод уже подменен. Эта операция называется перенаправлением стандартного вывода. В том случае, если файл уже существует, shell запишет его заново, полностью уничтожив все, что в нем содержалось до этого. Поэтому, чтобы продолжить записывать данные в textfile, потребуется другая операция - ">>":

 

Стандартный ввод

Стандартный ввод (standard input, stdin) - поток данных, открываемый системой для каждого процесса в момент его запуска и предназначенный для ввода данных.

Для передачи данных на вход программе может быть использован стандартный ввод (сокращенно - stdin). При работе с командной строкой стандартный ввод - это символы, вводимые пользователем с клавиатуры. Стандартный ввод можно перенаправить при помощи командной оболочки, подав на него данные из некоторого файла. Символ "<" служит для перенаправления содержимого файла на стандартный ввод программе. Например, если вызвать утилиту sort без параметра, она будет читать строки со стандартного ввода. Команда "sort < имя_файла" подаст на ввод sort данные из файла:

Результат действия этой команды аналогичен команде sort textfile - разница лишь в том, что когда используется "<", sort получает данные со стандартного ввода ничего не зная о файле "textfile", откуда они поступают. Механизм работы shell в данном случае тот же, что и при перенаправлении вывода: shell читает данные из файла "textfile", запускает утилиту sort и передает ей на стандартный ввод содержимое файла.

Необходимо помнить, что операция ">" деструктивна: она всегда создает файл нулевой длины. Поэтому для, допустим, сортировки данных в файле надо применять последовательно sort < файл > новый_файл и mv новый_файл файл. Команда вида команда < файл > тот_же_файл просто урежет его до нулевой длины!

Стандартный вывод ошибок

Для диагностических сообщений, информирующих пользователя о ходе выполнения работы: а также для сообщений об ошибках, возникших в ходе выполнения программы, в Linux предусмотрен стандартный вывод ошибок (сокращенно - stderr).

Стандартный вывод ошибок (standard error, stderr) - поток данных, открываемый системой для каждого процесса в момент его запуска и предназначенный для диагностических сообщений, выводимых процессом.

Использование стандартного вывода ошибок наряду со стандартным выводом позволяет отделить собственно результат работы программы от разнообразной сопровождающей информации, например, направив их в разные файлы. Стандартный вывод ошибок может быть перенаправлен так же, как и стандартный ввод/вывод, для этого используется комбинация символов "2>", например  info cat > cat.info 2> cat.stderr

 На терминал в этом случае ничего не попадет - стандартный вывод отправится в файл cat.info, стандартный вывод ошибок - в cat.stderr. Вместо ">" и "2>" можно было бы написать "1>" и "2>". Цифры в данном случае обозначают номера дескрипторов открываемых файлов. Если некая утилита ожидает получить открытый дескриптор с номером, допустим, 4, то, для того чтобы ее запустить, обязательно потребуется использовать сочетание "4>".

Иногда, однако, требуется объединить стандартный вывод и стандартный вывод ошибок в одном файле, а не разделять их. В командной оболочке bash для этого имеется специальная последовательность "2>&1". Это означает "направить стандартный вывод ошибок туда же, куда и стандартный вывод":

 

Перенаправление в никуда

Иногда заведомо известно, что какие-то данные, выведенные программой, не понадобятся. Например, предупреждения со стандартного вывода ошибок. В этом случае можно перенаправить стандартный вывод ошибок на устройство, специально предназначенное для уничтожения данных - /dev/null. Все, что записывается в этот файл, просто будет выброшено и нигде не сохранится:

Пример 7.2. Перенаправление в /dev/null

[student@localhost root]$ info cat > cat.info 2> /dev/null

Точно таким же образом можно избавиться и от стандартного вывода, отправив его в /dev/null.

 

1.2. Обработка данных в потоке. Конвейер

Нередко возникают ситуации, когда нужно обработать вывод одной программы какой-то другой программой. Для решения подобной задачи в bash предусмотрена возможность перенаправления вывода  можно не только в файл, но и непосредственно на стандартный ввод другой программе. В Linux такой способ передачи данных называется конвейер.

В bash для перенаправления стандартного вывода на стандартный ввод другой программе служит символ "|". Самый простой и наиболее распространенный случай, когда требуется использовать конвейер, возникает, если вывод программы не умещается на экране монитора и очень быстро "пролетает" перед глазами, так что человек не успевает его прочитать. В этом случае можно направить вывод в программу просмотра (less).

Можно последовательно обработать данные несколькими разными программами, перенаправляя вывод на ввод следующей программе и организуя сколь угодно длинный конвейер для обработки данных. В результате получаются командные строки вида "cmd1 | cmd2 | ... | cmdN".

Организация конвейера устроена в shell по той же схеме, что и перенаправление в файл, но с использованием особого объекта системы - канала. Можно представить трубу, немедленно доставляющую данные от входа к выходу (английский термин - "pipe"). Каналом пользуются сразу два процесса: один пишет туда, другой читает. Связывая две команды конвейером, shell открывает канал (заводится два дескриптора - входной и выходной), подменяет по уже описанному алгоритму стандартный вывод первого процесса на входной дескриптор канала, а стандартный ввод второго процесса - на выходной дескриптор канала. После чего остается запустить по команде в этих процессах, и стандартный вывод первой попадет на стандартный ввод второй.

Канал (pipe) - неделимая пара дескрипторов (входной и выходной), связанных друг с другом таким образом, что данные, записанные во входной дескриптор, будут немедленно доступны на чтение с выходного дескриптора.

 

1.3 Фильтры

Если программа и вводит данные, и выводит, то ее можно рассматривать как трубу, в которую что-то входит и из которой что-то выходит. Обычно смысл работы таких программ заключается в том, чтобы определенным образом обработать поступившие данные. В Linux такие программы называют фильтрами: данные проходят через них, причем что-то "застревает" в фильтре и не появляется на выходе, а что-то изменяется, что-то проходит сквозь фильтр неизменным. Фильтры в Linux обычно по умолчанию читают данные со стандартного ввода, а выводят на стандартный вывод. Простейший фильтр - программа cat: собственно, никакой "фильтрации" данных она не производит, она просто копирует стандартный ввод на стандартный вывод.

Данные, проходящие через фильтр, представляют собой текст: в стандартных потоках ввода-вывода все данные передаются в виде символов, строка за строкой, как и в терминале. Поэтому могут быть состыкованы при помощи конвейера ввод и вывод любых двух программ, поддерживающих стандартные потоки ввода-вывода.

В любом дистрибутиве Linux присутствует набор стандартных утилит, предназначенных для работы с файловой системой и обработки текстовых данных. Это who, cat, ls, pwd, cp, chmod, id, sort и др. Каждая из этих утилит предназначена для исполнения какой-то одной операции над файлами или текстом: вывод списка файлов в каталоге, копирование, сортировка строк, хотя каждая утилита может выполнять свою функцию по-разному, в зависимости от переданных ей ключей и параметров. При этом все они ориентированы на работу с данными в текстовой форме, многие являются фильтрами, не имеют графического интерфейса, вызываются из командной строки и работают со стандартными потоками ввода/вывода, поэтому хорошо приспособлены для построения конвейеров.

1.4 Структурные единицы текста

Работу в системе Linux почти всегда можно представить как работу с текстами. Поиск файлов и других объектов системы - это получение от системы текста особой структуры - списка имен. Операции над файлами - создание, переименование, перемещение, а также сортировка, перекодировка и прочее - замену одних символов и строк другими либо в каталогах, либо в самих файлах. Работая с текстом в Linux, нужно принимать во внимание, что текстовые данные, передаваемые в системе, структурированы. Большинство утилит обрабатывает не непрерывный поток текста, а последовательность единиц. В текстовых данных в Linux выделяются следующие структурные единицы:

1.  Строки

Строка - основная единица передачи текста в Linux. Терминал передает данные от пользователя системе строками (командная строка), множество утилит вводят и выводят данные построчно, при работе многих утилит одной строке соответствует один объект системы (имя файла, путь и т. п.), sort сортирует строки. Строки разделяются символом конца строки "\n" (newline).

2.  Поля

В одной строке может упоминаться и больше одного объекта. Если понимать объект как последовательность символов из определенного набора (например, букв), то строку можно рассматривать как состоящую из слов и разделителей. В этом случае текст от начала строки до первого разделителя - это первое поле, от первого разделителя до второго - второе поле и т. д. В качестве разделителя можно рассматривать любой символ, который не может использоваться в объекте. Например, если в пути "/home/student" разделителем является символ "/", то первое поле пусто, второе содержит слово "home", третье - "student". Некоторые утилиты позволяют выбирать из строк отдельные поля (по номеру) и работать со строками как с таблицей.

3.  Символы

Минимальная единица текста - символ. Символ - это одна буква или другой письменный знак. Стандартные утилиты Linux позволяют заменять одни символы другими (производить транслитерацию), искать и заменять в строках символы и комбинации символов.

Символ конца строки в кодировке ASCII совпадает с управляющей последовательностью "^J" - "перевод строки", однако в других кодировках он может быть иным. Кроме того, на большинстве терминалов - но не на всех! - вслед за переводом строки необходимо выводить еще символ возврата каретки ("^M"). Это вызвало путаницу: некоторые системы требуют, чтобы в конце текстового файла стояли оба этих символа в определенном порядке. Чтобы избежать путаницы, в Linux было принято единственно верное решение: содержимое файла соответствует кодировке, а при выводе на терминал концы строки преобразуются в управляющие последовательности согласно настройке терминала.

В распоряжении пользователя Linux есть ряд утилит, выполняющих элементарные операции с единицами текста: поиск, замену, разделение и объединение строк, полей, символов. Эти утилиты, как правило, имеют одинаковое представление о том, как определяются единицы текста: что такое строка, какие символы являются разделителями и т. п. Во многих случаях их представления можно изменять при помощи настроек. Поэтому такие утилиты легко взаимодействуют друг с другом. Комбинируя их, можно автоматизировать довольно сложные операции по обработке текста.

 

1.5 Регулярные выражения

Регулярными выражениями называются особым образом составленные наборы символов, выделяющие из текста нужное сочетание слов или символов, которое соответствует признакам, отраженным в регулярном выражении. Иными словами, регулярное выражение — это фильтр для текста.

В Linux регулярные выражения используются командой grep, которая позволяет искать файлы с определенным содержанием либо выделять из файлов строки с необходимым содержимым (например, номера телефонов, даты и т. д.). Многие программы, так или иначе работающие с текстом, (текстовые редакторы), поддерживают регулярные выражения. К таким программам относятся два "главных" текстовых редактора Linux - Vim и Emacs. Однако нужно учитывать, что в разных программах используются разные диалекты языка регулярных выражений, где одни и те же понятия имеют разные обозначения, поэтому всегда нужно обращаться к руководству по конкретной программе.

 

1.5.1 Элементарные регулярные выражения

Элементарная структурная единица регулярного выражения — это символ. Текст можно искать по определенному набору букв и цифр. Рассмотрим пример, в котором с помощью регулярного выражения выделим из данных строк те, которые содержат сочетание букв bc (именно в этом порядке):

Исходный набор строк:

abc

abcd

dcba

adbc

Регулярное выражение:

bc

Результат:

abc

abcd

adbc

 

1.5.2. Конструкция вида [...]

Рассмотрим другой пример, заменив некоторые буквы в строках на заглавные:

Исходный набор строк:

аbС

abcd

dcba

adBc

Регулярное выражение:

bc

Результат: abcd

Результат обработки строк с помощью регулярного выражения изменился. При использовании вышеуказанного регулярного выражения в большинстве случаев чаще всего различают строчные и прописные буквы, что логически обосновано, если не указан соответствующий параметр, предписывающий не различать их. Программы, которые работают с регулярными выражениями чаще всего различают строчные и прописные буквы, если не указана соответствующая опция, предписывающая не различать строчные и прописные буквы.

В результате получается всего одна строка — вторая. Для вывода трех строк, как в первом примере, понадобится особая конструкция. Рассмотрим ее.

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

Исходный набор строк:

аbС

abcd

dcba

adBc

Регулярное выражение:

[Вb][Сc]

Результат:

аbС

abcd

adBc

Теперь рассмотрим такой пример: с помощью регулярного выражения необходимо выделить из указанных в предыдущем примере строк те, которые содержат некоторую букву английского алфавита в нижнем регистре и сразу за ней — букву с в нижнем или верхнем регистре. При применении способа перечисления для решения поставленной задачи получится следующее регулярное выражение:

[abcdefghijklmnopqrstuvwxyz] [Cc]

Это верно, но строка получилась длинной, что особенно неудобно при составлении больших регулярных выражений. В подобных случаях можно перечислить все эти 26 символов короче, используя интервалы, то есть указать начальный и конечный символы, поставив между ними знак «тире». Рассмотрим пример:

Исходный набор строк:

аbС

abcd

dcba

adBc

Регулярное выражение:

[a-z][Cc]

Результат:

аbС

abcd

dcba

Здесь a-z — это и есть нужный интервал. Можно изменить пример так, чтобы первый символ мог быть как строчным, так и прописным, для чего сразу после первого интервала указываем второй:

Исходный набор строк:

аbС

abcd

dcba

adBc

Регулярное выражение:

[a-zA-Z][Cc]

Результат:

аbС

abcd

dcba

adBc

То же касается и цифр. Границами интервала могут быть любые символы, но последовательности типа [z-a] и [5-1] смысла иметь не будут, так как ASCII-код первого символа должен быть меньше либо равен коду завершающего. По поводу интервалов с цифрами следует напомнить, что символ нуля идет раньше символов всех остальных цифр. Количество стоящих рядом последовательностей неограниченно.

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

Из данного набора строк выделим только те, в которых буква b стоит перед любым символом, кроме буквы с.

Исходный набор строк:

аbС

abcd

dcba

adBc

Регулярное выражение:

[Bb][^Cс]

Результат:

dcba

 

1.5.3. Метасимволы

 

Не все символы можно использовать прямо по назначению. Посмотрите, например, на конструкцию, которая описывалась в предыдущем разделе. Допустим, требуется найти в каком-то файле строки, содержащие следующий набор символов: abc[def. Можно предположить, что регулярное выражение будет составлено по принципам, описанным выше, но это неверно. Открывающая квадратная скобка — это один из символов, который несет для программы, работающей с регулярными выражениями, особый смысл (который был рассмотрен ранее). Такие символы называются метасимволами.

Метасимволы бывают разными и служат для различных целей. Из предыдущего материала можно выделить метасимволы открывающей и закрывающей квадратных скобок, а также символ ^. Символ «тире» не рассматривается как метасимвол, так как он имеет особое значение только внутри конструкции с квадратными скобками, а вне такой конструкции специально не применяется.

Рассмотрим те метасимволы, которые предполагают, что в конечном выражении на их месте будет стоять какой-либо символ или символы (табл. 7.1).

 

Таблица 7.1. Знакозаменяющие метасимволы

Метасим-вол

Описание метасимвола

.(точка)

 

Предполагает, что в конечном выражении на ее месте будет стоять любой символ. Продемонстрируем это на примере набора английских слов:

Исходный набор строк:

wake

make

machine

cake

maze

Регулярное выражение:

ma.e

Результат:

make

maze 

\w

Замещает любые символы, которые относятся к буквам, цифрам и знаку подчеркивания. Пример:

Исходный набор строк:

abc

а$с

a1c

а с

Регулярное выражение:

a\wc

Результат:

abc

a1c

\W

Замещает все символы, кроме букв, цифр и знака подчеркивания (то есть является обратным метасимволу \w). Пример:

Исходный набор строк:

abc

а$с

a1c

а с

Регулярное выражение:

a\Wc

Результат:

а$с а с

\d

Замещает все цифры. Продемонстрируем его действие на том же примере:

 Исходный набор строк:

abc

а$с

a1c

а с

Регулярное выражение:

a\dc

Результат:

alc

\D

Замещает все символы, кроме цифр, например:

Исходный набор строк:

abc

а$с

alc

а с

\D

Регулярное выражение:  a\Dc

Результат:

abc

а$с

а с

[\b]

Замещает символ перевода курсора на один влево (возврат курсора)

\r

Замещает символ перевода курсора в начало строки

\n

Замещает символ переноса курсора на новую строку

\t

Замещает символ горизонтальной табуляции

\v

Замещает символ вертикальной табуляции

\f

Замещает символ перехода на новую страницу

\s

Равнозначен использованию пяти последних метасимволов, то есть вместо метасимвола \s можно написать [\r\n\t\v\f ], что, однако, не так удобно

\S

Является обратным метасимволу \ s

     

 

Для лучшего понимания рассмотрим, что такое символ перевода курсора в начало строки, переноса курсора на новую строку и т. д. В конце каждой строки находятся один или два символа, которые указывают на необходимость перехода на новую строку. Начиная с операционной системы MS-DOS и до настоящего времени в операционных системах Microsoft используются два символа для обозначения переноса строки — сам символ переноса и символ возврата курсора в начало строки (заменяются метасимволами \n и \r соответственно), хотя в отдельности эти символы почти не применяются. В Linux используется только символ перехода на новую строку (заменяется метасимволом \n). Это следует учесть при составлении регулярных выражений.

Рассмотрим интересную группу символов, для чего поставим следующую задачу. Количество символов, которые должны быть в конечном тексте, не всегда известно (примером может быть распознавание имени веб-сайта), поэтому при помощи вышеописанных конструкций и метасимволов написать действительно универсальные регулярные выражения невозможно. Рассмотрим еще одну группу символов, которые помогут решить подобные проблемы. Они используются сразу после символа, метасимвола либо конструкции, количество вхождения которых они должны описать (табл. 7.2).

 

Таблица 7.2. Метасимволы количества повторений

Символ

Описание метасимвола

?

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

Исходный набор строк:

acbd

aabc

caab

ecad

bcde

abac

Регулярное выражение:

b[Aa]?c

Результат:

aabc

bcde

abac

*

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

Исходный набор строк:

а123а

а12а

al23b

alc3b

b12а

aaa

Регулярное выражение:

[Aa]\d*[Aa]

Результат:

al23a

al2a

aaa

+

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

Исходный набор строк:

а123а а12а al23b alc3b  b12а  aaa

Регулярное выражение:

[Аа]\d+[Aa]

Результат:

а123а а12а

{min, max}

Иногда первых трех способов указания количества вхождений бывает недостаточно, так как они описывают количество не детально. Решить проблему можно следующим способом. Для указания количества вхождений символа либо конструкции после них ставят открывающие фигурные скобки и пишут минимальное количество вхождений. Если это количество фиксированное (то есть должно быть не больше и не меньше вхождений символа), то скобку закрывают; если должно быть не меньше указанного количества вхождений, то ставят запятую и закрывают скобку; если существует предельное количество вхождений, то после запятой указывают его и закрывают скобку. Так, эквивалентом знаку вопроса является конструкция {0,1}, знаку звездочки — { 0, }, знаку «плюс» — {1, }. Рассмотрим пример:

Исходный набор строк:

а123а

а12а

al23b

alc3b

b12а

aaa

Регулярное выражение:

[Aa]\d{2,}[Аа]

Результат: 

а123а

а12а

 

Символ  \b

Описание метасимвола

Играет большую роль при разборе выражений. Его функция заключается в следующем. Обычно программы, которые работают с регулярными выражениями (в том числе и grep), ищут сходные выражения в тексте, не определяя, является ли выражение словом и может ли быть расположено конечное выражение в начале или конце слова. Однако часто требуется найти именно конкретное слово, что позволяет сделать данный метасимвол. Он ставится на том месте, где слово должно начинаться или заканчиваться. Рассмотрим пример, в котором попытаемся в данном наборе слов найти начинающиеся на букву s и заканчивающиеся на букву r (для сравнения здесь будут приведены примеры регулярного выражения с использованием метасимвола /b и без него):

Исходный набор строк:

starfish

starless