Предисловие
Эта книга посвящена регулярным выражениям - мощному средству обработки текстов.
В ней вы научитесь использовать регулярные выражения на практике и извлекать максимум пользы из тех программ, в которых они поддерживаются. Впрочем, этим дело не ограничивается - вы овладеете регулярными выражениями на действительно мастерском уровне.
При работе с компьютером регулярные выражения могут использоваться очень часто (хотя возможно, вы об этом даже не подозреваете). В поисковых системах World Wide Web, редакторах, конфигурационных сценариях и системных утилитах регулярные выражения нередко поддерживаются в виде «возможностей для опытных пользователей». В таких языках, как Awk, Elisp, Expect, Perl, Python и Tcl, реализована встроенная поддержка регулярных выражений (и работа многих программ, написанных на этих языках, основана на регулярных выражениях), а для большинства других языков существуют библиотеки регулярных выражений. Например, вскоре после появления языка Java была написана библиотека регулярных выражений, бесплатно распространяемая через Web. Регулярные выражения поддерживаются в многих редакторах и средах программирования — vi, Delphi, Emacs, Brief, Visual C++, Nisus Writer и в многих других. Словом, область применения регулярных выражений очень широка.
Поддержка регулярных выражений в столь разнородных приложениях объясняется тем, что регулярные выражения обладают исключительно богатыми возможностями. На низком уровне регулярное выражение описывает некий фрагмент текста. Им можно воспользоваться для проверки данных, введенных пользователем, или, например, для фильтрации больших объемов данных. На более высоком уровне регулярные выражения позволяют управлять данными. Вы полностью контролируете свои данные и заставляете их работать на себя.
Возникает впечатление, что при столь широкой распространенности, популярности и непревзойденном богатстве возможностей регулярные выражения всегда и везде используются в полной мере. Наверняка они хорошо документированы, и уж точно существуют учебники как для новичков, так и для знатоков, желающих расширить свой кругозор.
К сожалению, это не так. Впрочем, документации по регулярным выражениям хватает, и существует она относительно давно (я прочитал свой первый учебник по регулярным выражениям в 1981 году). Однако вся документация традиционно ограничивается работой на «низком уровне», о котором я совсем недавно упоминал. Можно сколько угодно рассуждать о том, как краска накладывается на холст, и анализировать научные принципы смешения цветов — великим художником от этого не станешь. Чтобы добиться успехов в живописи, как и в любом виде искусства, необходимо учитывать человеческий фактор. Регулярные выражения, состоящие из служебных символов и текста, на первый взгляд кажутся холодными порождениями чистой науки, но лично я твердо полагаю, что они в основном были созданы правым полушарием мозга. Регулярные выражения открывают простор для творчества и изобретательности, и с их помощью можно создавать действительно элегантные решения.
Честно говоря, я не обладаю никакими талантами в том, что принято называть искусством. Я часто посещаю караоке-бары в Киото, но отсутствие слуха приходится компенсировать громкостью. И все же, когда мне удается найти элегантное решение трудной задачи, я чувствую себя настоящим творцом. В моей работе элегантные решения часто строятся именно на базе регулярных выражений. Поскольку мои немногочисленные творческие проявления часто связаны именно с регулярными выражениями, я стал к ним особенно неравнодушен. Работая над книгой, я хотел поделиться этой страстью с вами.
Книга представляет интерес для всех, кто мог бы использовать регулярные выражения в своей работе. Если вы еще не представляете, насколько богатыми возможностями обладают регулярные выражения, для вас откроется целый новый мир. Многие кросс-платформенные программы и языки, упоминаемые в книге, существуют в бесплатных версиях для MacOS, DOS/Windows, Unix, VMS и на других платформах. Информация о том, где их найти, приведена в приложении А.
Все пользователи GNU Emacs и vi, все программисты, работающие на Perl, Tcl, Python или Awk, найдут здесь многочисленные технические подробности, рекомендации, советы, а самое главное — понимание новых возможностей, которые можно немедленно применить на практике. Столь подробного и скрупулезного изложения материала вы просто не найдете в других источниках.
Регулярные выражения — это абстрактная концепция, которая по-разному реализуется в разных программах (причем далеко не все реализации представлены в этой книге). Если вы поймете общую концепцию регулярных выражений, освоить конкретную реализацию будет не так уж трудно. Этот принцип заложен в основу всей книги, поэтому большая часть изложенного материала выходит за рамки конкретных программ, использованных в примерах.
Эта книга может стать учебником, справочником или повестью — все зависит от того, как к ней подойти. Читатели, знакомые с регулярными выражениями, обычно рассматривают книгу как подробный справочник и сразу переходят к разделу, посвященному их любимой программе. Я не рекомендую так поступать.
Чтобы извлечь максимум пользы из этой книги, сначала прочитайте ее как повесть. По своему опыту знаю, что некоторые привычки и направления мысли способствуют более полному пониманию материала, однако подобные вещи должны усваиваться, а не запоминаться.
Небольшой тест — попробуйте определить понятие «между». Помните: определяемое слово не может использоваться в определении! Ну как, получилось? Нет? Действительно, задача не из простых. Хорошо, что смысл этого слова понятен всем, иначе нам пришлось бы подолгу разъяснять его всем несведущим. Даже такие простые концепции бывает трудно описать кому-то, кто еще не знаком с ними.
До определенной степени сказанное относится и к регулярным выражениям. На самом деле регулярные выражения не так сложны, как их описания и объяснения. Я разработал определенный стиль изложения, начинающийся с главы 1, и надеюсь, что вы начнете читать именно с этой главы. Некоторые описания действительно сложны; не огорчайтесь, если некоторые разделы придется читать дважды. Говорят, практический опыт — 9/10 закона (или что-то в этом роде), поэтому вам придется потратить немало времени и усилий, прежде чем перед вами начнет вырисовываться общая картина.
Повесть, рассказанная в этой книге, содержит множество подробностей. После того, как вы прочитаете ее и получите общее представление о предмете, книгой также можно пользоваться как справочником. Я не скупился на перекрестные ссылки и постарался сделать так, чтобы алфавитный указатель приносил наибольшую пользу.
До полного знакомства с материалом книги вряд ли можно работать с ней как со справочником. Конечно, вы можете заглянуть в одну из сводных таблиц (например, в огромную таблицу на стр. <$R[P#,R6-1]>) и решить, что в ней содержится вся необходимая информация. Однако огромное количество полезных сведений находится не в таблице, а в сопровождающем ее тексте. После знакомства с материалом вы начнете ориентироваться в рассматриваемых вопросах и поймете, какие сведения можно просто запомнить, а к каким придется возвращаться снова и снова.
Семь глав этой книги условно делятся на три логических части:
Вводная часть
В главе 1 представлены основные концепции регулярных выражений.
В главе 2 рассматривается применение регулярных выражений при обработке текста.
В главе 3 приводится обзор диалектов регулярных выражений, а также некоторые исторические сведения.
Подробное описание
В главе 4 подробно рассмотрен механизм обработки регулярных выражений.
В главе 5 проанализированы некоторые последствия и практические применения материала главы 4.
Конкретные программы
Глава 6 посвящена специфике диалектов регулярных выражений в некоторых распространенных программах.
В главе 7 описаны все аспекты Perl, относящиеся к работе с регулярными выражениями.
Приложения
В приложении А вы узнаете, где найти многие программы, упоминаемые в книге.
В приложении Б приведен полный текст программы, созданной в главе 7.
Первая часть книги дает новичкам представление о рассматриваемой теме. Более опытные читатели могут пропустить начальные главы, хотя я настоятельно рекомендую прочитать главу 3 даже самым закаленным ветеранам.
l Глава 1, «Знакомство с регулярными выражениями», написана для стопроцентного новичка. Читатель познакомится с концепцией регулярных выражений на примере распространенной программы egrep. Я постарался изложить свое видение того, как мыслить регулярными выражениями, закладывая тем самым надежную основу для нетривиального материала следующих глав. Даже читателям, имеющим опыт работы с регулярными выражениями, стоит просмотреть первую главу.
l В главе 2, «Практическое применение регулярных выражений, рассматривается практическая обработка текста в языках программирования с поддержкой регулярных выражений. Дополнительные примеры помогут лучше разобраться в сложном материале следующих глав и продемонстрируют некоторые важные принципы мышления, используемые при построении сложных регулярных выражений. Чтобы читатель лучше представил, как «мыслить регулярными выражениями», в этой главе будет рассмотрена нетривиальная задача и показаны пути ее решения в двух разных программах с поддержкой регулярных выражений.
l В главе 3, «Регулярные выражения: диалекты и возможности», приведен обзор всевозможных диалектов регулярных выражений, встречающихся в современных программах. Эволюция регулярных выражений проходила довольно бурно, поэтому многие диалекты, распространенные в наши дни, заметно отличаются друг от друга. В этой главе описана история, а также процесс эволюции регулярных выражений и тех программ, в которых они используются. В конце главы приведен краткий «Путеводитель по серьезным главам». Это своего рода «карта», при помощи которой вы сможете извлечь максимум пользы из нетривиального материала следующих глав.
Разобравшись с основными принципами, мы переходим к поиску ответов на вопросы «как?» и «почему?». При полном понимании этого материала вы сможете применять полученные знания везде, где регулярные выражения приносят пользу.
l Глава 4, «Механика обработки регулярных выражений», начинает изложение основного материала этой книги. В ней важные принципы внутренней работы механизма регулярных выражений рассматриваются с практической точки зрения. Тот, кто разберется во всех тонкостях процесса обработки регулярных выражений, пройдет большую часть пути к вершинам мастерства.
l В главе 5, «Построение регулярных выражений», рассматриваются специфические аспекты механизмов регулярных выражений, реализованных в популярных утилитах и языках программирования — Perl, sed, grep, Tcl, Python, Expect, Emacs и т. д. Руководствуясь материалом, подробно изложенным в главе 4, вы научитесь использовать сильные стороны каждого механизма и узнаете, как обходить их недостатки.
Если вы хорошо усвоили материал глав 4 и 5, то разобраться в специфике любой конкретной реализации обычно бывает несложно. Впрочем, одному замечательному исключению — языку Perl — я посвятил целую главу. И все же реализации отличаются друг от друга, и при работе с ними приходится учитывать некоторые важные особенности.
l В главе 6, «Регулярные выражения в конкретных программах», рассматриваются специфические особенности разных программ. Особое внимание уделяется тем возможностям, которые изменяются от программы к программе. Программы awk, Tcl и GNU Emacs рассматриваются более подробно, чем в предыдущих общих главах.
l Глава 7, «Регулярные выражения в Perl», посвящена языку Perl — вероятно, самому популярному из всех современных языков программирования с поддержкой регулярных выражений. В Perl существует всего три оператора для работы регулярными выражениями, однако из-за бесчисленных режимов, особых случаев и т. д. перед программистом открываются широчайшие возможности — в которых кроются многочисленные ловушки. Богатство возможностей, позволяющее быстро перейти от концепции к программе, превращается в «минное поле» для начинающих программистов. Надеюсь, подробное изложение материала этой главы поможет вам преодолеть все трудности.
<$M[R0-1]>При подробном описании сложных операций с текстом необходима точность (как, впрочем, и при выполнении этих операций). Всего один лишний или недостающий пробел может иметь колоссальные последствия, поэтому я буду использовать в книге некоторые условные обозначения.
l Регулярные выражения обычно выглядят так: [this]. Обратите внимание на тонкие «уголки»— они означают, что перед вами регулярное выражение. Литеральный текст (например, тот, который вы ищете) обычно заключается в кавычки-«елочки»: «this». Иногда я буду опускать уголки или кавычки, если это заведомо не приведет к недоразумениям. Фрагменты программного кода всегда представляются в естественном виде, поэтому уголки и «елочки» в них не используются.
l Без специальной записи довольно трудно сказать, сколькими пробелами разделены буквы в строке «a b». По этой причине пробелы, присутствующие в регулярном выражении (а иногда и в литеральном тексте), изображаются символом «spc». При такой записи становится очевидно, что строка «aspcspcspcspcb» содержит четыре пробела.
Я также буду использовать визуальные обозначения для символов табуляции и новой строки:
spc |
пробел |
tab |
символ табуляции |
new |
символ новой строки |
l Время от времени я использую подчеркивание или темный фон для выделения частей литерального текста или регулярного выражения. Например:
…поскольку [cat] совпадает с текстом «Itspcindicatesspcyourspccatspcis…», а не со словом cat…
В данном случае подчеркиванием выделяется фактически совпавший текст. Можно привести другой пример:
…чтобы использовать это выражение, можно заключить [Subject|Date] в круглые скобки и добавить двоеточие с пробелом. Результат выглядит так: [(Subject|Date):spc].
На этот раз подчеркиваются символы, добавленные в выражение после упоминания в тексте.
l В литеральном тексте и регулярных выражениях используются разные многоточия. Например, […] означает пару квадратных скобок с произвольным содержимым, а [. . .] — последовательность из трех точек.
Время от времени — особенно в начальных главах — я задаю вопросы, напрямую связанные с обсуждаемыми концепциями. Не стоит полагать, что задачи просто занимают место в книге; я действительно хочу, чтобы вы решили их, прежде чем двигаться дальше. Пожалуйста, не ленитесь. Я постарался не преувеличивать их значение, и задачи встречаются не так уж часто. Кроме того, по ним можно оценить степень понимания материала. Если для решения задачи вам потребовалось больше нескольких секунд, вероятно, перед тем, как продолжать чтение, лучше вернуться к соответствующему разделу и перечитать его заново.
Я постарался по возможности упростить поиск ответов. Все, что от вас требуется — перевернуть страницу. Ответы на вопросы, помеченные знаком ref, всегда находятся на следующей странице. Пока вы размышляете над вопросом, ответы не видны, но добраться до них проще простого.
Однажды моя мама сказала, что до свадьбы с отцом она лишь думала, что любит его. Эта любовь, продолжала она, не идет ни в какое сравнение с тем глубоким чувством, которое они испытывают друг к другу сейчас, после тридцати лет совместной жизни. Они даже не представляли, насколько лучше станет их жизнь.
Возможно, эта аналогия выглядит несколько мелодраматично, но несколько лет назад я тоже думал, что разбираюсь в регулярных выражениях. Я использовал их в течение нескольких лет, программировал на awk, sed и Perl и даже написал относительно полный пакет для работы с регулярными выражениями на японском языке. Я не особенно разбирался в теории, а просто вывел основные закономерности самостоятельно. Тем не менее, благодаря своим знаниям я заслужил репутацию местного знатока в электронной конференции Perl. Некоторые из своих публикаций я предал своему другу, Джеку Халперну (Jack Halpern), который в это время изучал Perl. Он часто предлагал мне написать книгу, но я никогда не относился к этому предложению серьезно — ведь Джек сам был автором десятка книг (причем на разных языках!). Когда такой человек предлагает написать книгу, это выглядит так, словно Карл Льюис предлагает «просто прыгнуть подальше». Да, сказать-то легко!
В конце июня 1994 года наш общий друг, Кен Лунде (Ken Lunde), тоже предложил мне написать книгу. Кен также работал на издательство O’Reilly, и упускать такую возможность было непростительно. Меня представили Энди Ораму (Andy Oram), который стал моим редактором и руководителем проекта.
Вскоре я понял, как мало я знаю на самом деле.
Моя книга должна была выйти за рамки узкого круга программ, с которыми я работал, и я решил потратить немного времени на исследования более общих принципов использования регулярных выражений. Так началась моя одиссея, которая заняла почти два года. Только для того, чтобы лучше понять диалекты регулярных выражений, я в конечном счете создал комплекс тестовых программ, который был реализован в виде сценария, содержавшего более 60 000 строк. Я тестировал десятки программ. Я сообщал о многочисленных ошибках, выявленных моим сценарием (многие из этих ошибок были впоследствии исправлены). Главный принцип моей работы кратко сформулировал Кен Лунде: «ты занимаешься исследованиями для того, чтобы избавить от этого своих читателей».
Поначалу я считал, что работа над проектом займет не больше года. Господи, как же я ошибался! Помимо исследований, необходимых из-за моего невежества, несколько месяцев было потеряно из-за землетрясения в Кобэ. Наконец, я написал и отправил в корзину два варианта этой книги, пока не почувствовал, что у меня вышло нечто достойное. Оказалось, написание книги и публикация сообщений в Usenet — совсем не одно и то же. Работа заняла почти два с половиной года.
В начале моих исследований в области как самих регулярных выражений, так и их истории, мне сильно повезло — я знал людей, к которым можно было обратиться за помощью. На ранней стадии работы над книгой Том Вуд (Tom Wood) из Cygnus Systems открыл мне глаза на различные варианты реализации механизма регулярных выражений. Мне также сильно помогли Верн Паксон (Vern Paxon), автор flex, и Генри Спенсер (Henry Spencer) — бог в области регулярных выражений.
Сведениями о тех далеких годах, когда регулярные выражения еще не вошли в мир компьютеров, я обязан Роберту Констейблу (Robert Constable) и Энилу Нероду (Anil Nerode). Я хочу поблагодарить Брайана Кернигана — соавтора awk, Кена Томпсона — автора ed и одного из соавторов Unix, Майкла Леска (Michael Lesk) — автора lex, Джеймса Гослинга (James Gosling) — автора первой версии Emacs для Unix, в которой впервые появилась поддержка регулярных выражений, Ричарда Столмена (Richard Stallman) — автора исходной версии Emacs и текущего автора GNU Emacs, Ларри Уолла — автора rn, patch и Perl, Марка Биггара (Mark Biggar) — дядюшки Perl по материнской линии, и Дона Либса (Don Libes) — автора «Life with Unix» и других книг, за информацию о том, как начиналась компьютерная история регулярных выражений.
Работа множества рецензентов помогла мне избавить книгу от большого количества ошибок. Первую линию обороны держал мой редактор, Энди Орам, который без устали работал над продвижением проекта в нужном направлении. Благодаря подробным рецензиям Джека Халперна на ранние варианты этой книги вы никогда их не увидите. В те месяцы, когда работа над окончательным вариантом книги близилась к завершению, Уильям Ф.Матон (William F.Maton) посвятил бесчисленные часы рецензированию нескольких версий разных глав. Подробная рецензия даже на одну главу — уже большая услуга, а Уильям пошел намного дальше. В своей рецензии Кен Лунде привел множество замечаний, заметно улучшивших мой английский языка (официальную правку последних версий выполнял Стив Клейндлер (Steve Kleindler), от которого я узнал об английском языке больше, чем за 12 лет обязательного обучения). На обработку 25 страниц подробных, содержательных комментариев от Уэйна Берка (Wayne Berke) у меня ушло несколько недель, однако они значительно повысили качество текста. В своей рецензии Том Кристиансен показал себя как специалист не только в области Perl, но и лингвистики: от него я тоже узнал кое-что новое об английском языке. К дискуссии, возникшей при обсуждении рецензии Тома, вскоре присоединился Ларри Уолл; он обнаружил еще несколько серьезных ляпов из области Perl. Майк Сток (Mike Stok), Джон Оруэнт (John Orwant) и Генри Спенсер также помогли мне своими подробными рецензиями (я особенно благодарен Генри за то, что он исправил некоторые мои заблуждения в области теории). Майк Чачич (Mike Chachich) и Тим О’Рейли (Tim O’Reilly) также внесли ценные замечания.
Мнение знатоков — великое дело, но для книги, предназначенной для обучения, также важно и мнение простого читателя. Джек Халперн помог мне в работе над ранними вариантами книги, а Норрис Коуч (Norris Couch) и Пол Берд (Paul Beard) взялись за тестирование программ из окончательной версии. Их полезные комментарии помогли мне заполнить некоторые из оставшихся пробелов.
Даже после работы всех рецензентов и несмотря на все мои усилия, вероятно, в этой книге остались некоторые ошибки. Пожалуйста, учтите, что ни один из рецензентов не работал с окончательным вариантом рукописи, и в некоторых случаях я не соглашался с мнением рецензентов. Усердный труд рецензентов заслуживает величайшей благодарности, однако ошибки могли быть внесены и после написания рецензии, поэтому за все ошибки в этой книге отвечаю я один.
В приложении А также указан адрес, по которому можно найти последний вариант списка ошибок и опечаток. Надеюсь, этот список будет коротким.
Эта книга стала возможной благодаря организационной поддержке, оказанной некоторыми людьми. Кен Лунде из Adobe Systems создал специальные символы и шрифты для оформления книги[1]. Японские иероглифы взяты из гарнитуры Heisei Mincho W3, а корейские — из гарнитуры Munhwa Министерства культуры и спорта Южной Кореи.
Я очень долго трудился над созданием рисунков. Получилось неплохо. Потом за дело взялся Крис Рейли (Chris Reilly) и на скорую руку подправил графический стиль. Почти каждый рисунок в книге отмечен его прикосновением, за что я ему искренне благодарен.
Моя компания, Omron Corporation, и особенно Кейт Масуда (Keith Masuda) и Юкио Такасаки (Yukio Takasaki), поддерживали этот проект и поощряли меня к работе над ним. Наличие принтера с разрешением 900 dpi позволило использовать некоторые особые шрифты.
Я особенно благодарен Кендзи Аояма (Kenji Aoyama). При подготовке материала для окончательной правки на моем ноутбуке ThinkPad сломалась мышь. В невероятном порыве альтруизма Кендзи одолжил мне свой ThinkPad на несколько недель, пока в IBM занимались ремонтом. Спасибо!
Я так долго работал над этой книгой, что уже не помню, когда я в последний раз со спокойной совестью отдыхал целый день. Надеюсь, я смогу воспользоваться некоторыми радостями жизни (аккуратно сложенное постельное белье, наполненные формочки со льдом, чистый письменный стол), проведу несколько выходных за поездками по горам на мотоцикле, и возьму продолжительный отпуск.
Все это, конечно, хорошо, однако в мире регулярных выражений происходит много интересного, поэтому мой отдых будет недолгим. В то время, когда книга отправлялась в печать, шли активные дискуссии об усовершенствовании механизмов регулярных выражений Python и Perl. Последние новости вы найдете на моей Web-странице (см. приложение А).
Ваши замечания, предложения, вопросы отправляйте по адресу электронной почты comp@piter.com (издательство «Питер», компьютерная редакция).
Мы будем рады узнать ваше мнение!
Текст программы, приведенной в приложении Б, вы можете найти по адресу http://www.piter.com/download.
На Web-сайте издательства http://www.piter.com вы найдете подробную информацию о наших книгах.