Всё об использования регулярных выражений в calibre

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

Во-первых, слово предупреждения и слово мужества

Это неизбежно будет чем-то техническим, ведь регулярные выражения - это технический инструмент для выполнения технических задач. Мне придется использовать жаргон и понятия, которые могут показаться сложными или запутанными. Я попытаюсь объяснить эти концепции настолько ясно, насколько смогу, но на самом деле не могу обойтись без их использования. Это, как говорится, не обескураживайся никаким жаргоном, так как я пытался объяснить все новое. И хотя регулярные выражения сами по себе могут показаться тайной, черной магией (или, если быть более прозаичным, случайной цепочкой букв и знаков мумбо-юмбо), я обещаю, что они не так уж и сложны. Даже те, кто действительно хорошо понимает регулярные выражения, испытывают затруднения при чтении более сложных, но написание их не так сложно - вы строите выражение шаг за шагом. Итак, сделайте шаг и следуйте за мной в кроличью нору.

Где в calibre вы можете использовать регулярные выражения?

Есть несколько мест, где calibre использует регулярные выражения. В опциях конвертации есть :guilabel:«Поиск и замена», обнаружение метаданных по именам файлов в настройках импорта и «Поиск и замена» при массовом редактировании метаданных книг. Редактор книг calibre также может использовать регулярные выражения при поиске и замене. Наконец, вы можете использовать регулярные выражения при поиске в списке книг и при поиске в просмотрщике электронных книг.

Что на земле является регулярным выражением?

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

Примечание

Я использую здесь строку в том смысле, в каком она используется в языках программирования: строка из одного или нескольких символов, символов, включая действительные символы, цифры, знаки препинания и так называемые пробелы (переносы строк, табуляторы и т. д.). Обратите внимание, что обычно прописные и строчные буквы не считаются одинаковыми, поэтому «а» - это символ, отличный от «А» и т. д. В calibre регулярные выражения не чувствительны к регистру в строке поиска, но не в параметрах преобразования. Есть способ сделать каждое регулярное выражение нечувствительным к регистру, но мы обсудим это позже. Это усложняется тем, что регулярные выражения допускают изменения в соответствующих им строках, поэтому одно выражение может соответствовать нескольким строкам, поэтому люди вообще не хотят их использовать. Подробнее об этом чуть позже.

Хотите объяснений?

Ну, вот почему мы здесь. Во-первых, это самая важная концепция в регулярных выражениях: Сама строка является регулярным выражением, которое соответствует самому себе. То есть, если бы я хотел сопоставить строку "Hello, World!" С помощью регулярного выражения, используемое регулярное выражение было бы Hello, World!. И да, это действительно так просто. Однако заметьте, что это соответствует только точной строке "Hello, World!", а не, например. "Hello, wOrld!" или ``»hello, world!» `` или любому другому подобному варианту.

Это звучит не так уж плохо. Что дальше?

Следующее - начало действительно хороших вещей. Помните, где я говорил, что регулярные выражения могут соответствовать нескольким строкам? Здесь все становится немного сложнее. Скажем, в качестве более практичного упражнения электронная книга, которую вы хотели преобразовать, имела неприятный нижний колонтитул, подсчитывающий страницы, например «Страница 5 из 423». Очевидно, что номер страницы увеличится с 1 до 423, поэтому вам нужно будет сопоставить 423 различных строки, верно? Неправильно, на самом деле: регулярные выражения позволяют вам определять наборы символов, которые соответствуют: Чтобы определить набор, вы помещаете все символы, в набор в квадратных скобках. Так, например, набор [abc] будет соответствовать символу «a», «b» или «c». * Наборы всегда будут соответствовать только одному из символов в наборе *. Они «понимают» диапазоны символов, то есть, если вы хотите сопоставить все символы нижнего регистра, вы должны использовать набор [az] для символов нижнего и верхнего регистра, которые вы используете [a-zA -Z] и так далее. Есть идея? Таким образом, очевидно, что с помощью выражения Page [0-9] of 423 вы сможете сопоставить первые 9 страниц, сократив количество выражений до трех: Второе выражение Page [0-9] [0-9] of 423 будет соответствовать всем двузначным номерам страниц, и я уверен, что вы можете догадаться, как будет выглядеть третье выражение. Да, начинайте. Запишите это

Эй, аккуратно! Это начинает иметь смысл!

Я надеялся, что вы скажете это. Но приготовьтесь, теперь становится ещё лучше! Мы только что увидели, что используя наборы, мы можем сопоставить один из нескольких символов одновременно. Но вы даже можете повторить символ или набор, сократив число выражений, необходимых для обработки приведенного выше примера с номером страницы, до одного. Да, ОДИН! Вы в восторге? А должны быть! Это работает так: некоторые так называемые специальные символы «+», «?» и «*», повторяют единственный элемент, предшествующий им. (Элемент означает либо один символ, набор символов, escape-последовательность или группу (мы узнаем о последних двух позже), короче говоря, любой отдельный объект в регулярном выражении.) Эти символы называются подстановочными знаками или квантификаторами. Чтобы быть более точным, «?» соответствует 0 или 1 предыдущего элемента, «*» соответствует 0 или более предыдущего элемента, а «+» соответствует 1 или более предыдущего элемента. Несколько примеров: выражение a? будет соответствовать либо «» (это пустая строка, в данном случае не совсем полезная), либо «a», выражение a* будет соответствовать «», «a», «aa» или любому количеству a в строке, и, наконец, выражение a+ будет соответствовать «a», «aa» или любому количеству a в строке (Примечание: это не будет соответствует пустой строке!). То же самое для множеств: выражение [0-9]+ будет соответствовать каждому целому числу! Я знаю, о чем вы думаете, и вы правы: если вы используете это в приведенном выше случае совпадения номеров страниц, разве это не будет единственным выражением, которое будет соответствовать всем номерам страниц? Да, выражение Page [0-9]+ of 423 будет соответствовать каждому номеру страницы в этой книге!

Примечание

Примечание к этим квантификаторам: они обычно пытаются найти как можно больше текста, поэтому будьте осторожны при их использовании. Это называется «жадным поведением» - я уверен, вы понимаете, почему. Это становится проблематичным, когда вы, скажем, пытаетесь сопоставить тег. Рассмотрим, например, строку "<p class="calibre2">Title here</p>" и, допустим, вы хотите сопоставить открывающий тег (часть между первой парой угловых скобок, чуть подробнее о тегах позже). Можно подумать, что выражение <p.*> Будет соответствовать этому тегу, но на самом деле оно соответствует всей строке! (Символ «.» является другим специальным символом. Он соответствует чему угодно кроме переносов строк, поэтому, по сути, выражение .* будет соответствовать любой отдельной строке, о которой вы можете подумать.) Вместо этого попробуйте использовать <p.*?>, что делает квантификатор "*" не жадным. Это выражение будет соответствовать только первому открывающему тегу, как и предполагалось. На самом деле есть еще один способ сделать это: выражение <p[^>]*> будет соответствовать тому же открывающему тегу - вы поймете почему после следующего раздела. Просто отметьте, что довольно часто существует несколько способов написания регулярного выражения.

Ну, эти специальные символы очень аккуратные и все такое, но что, если я хочу сопоставить точку или знак вопроса?

Конечно, вы можете сделать это: просто поставьте обратную косую черту перед любым специальным символом, и он будет интерпретирован как буквальный символ без какого-либо специального значения. Эта пара с обратной косой чертой, за которой следует один символ, называется escape-последовательностью, а действие по добавлению обратной косой черты перед специальным символом называется экранированием этого символа. Экранирующая последовательность интерпретируется как отдельный элемент. Конечно, существуют escape-последовательности, которые делают больше, чем просто экранирование специальных символов, например, "\t" означает табулятор. Мы вернемся к некоторым escape-последовательностям позже. Да, и, кстати, в отношении этих специальных символов: рассмотрите любой символ, который мы обсуждаем в этом введении, как имеющий некоторую функцию, которая должна быть особенной и, следовательно, должна быть экранирована, если вы хотите буквальный символ.

Итак, какие наборы наиболее полезны?

Знал, что ты спросишь. Некоторые полезные наборы: [0-9] соответствует одному числу, [az] соответствует одной строчной букве, [AZ] соответствует одной заглавной букве, [a-zA-Z] сопоставление одной буквы и [a-zA-Z0-9] сопоставление одной буквы или числа. Также можете использовать escape-последовательность в качестве сокращения:

\d

эквивалентно [0-9]

\w

эквивалентно [a-zA-Z0-9_]

\s

эквивалентно любому пробелу

Примечание

«Пробел» (Whitespace) - это термин для всего, что не будет напечатано. Эти символы включают пробел (space), табулятор (tabulator), перевод строки (line feed), перевод формы (form feed), возврат каретки (carriage return), неразрывные пробелы (non-breaking spaces) и т.п.

Примечание

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

В качестве последней заметки о наборах вы также можете определить набор как любой символ, но из набора. Вы делаете это путем включения символа "^" в качестве самого первого символа в наборе. Таким образом, [^a] будет соответствовать любому символу, кроме «a». Это называется дополнением набора. Те сокращенные последовательности escape-последовательностей, которые мы видели ранее, также могут быть дополнены: "\D" означает любой нечисловой символ, что эквивалентно [^0-9]. Другие сокращения можно дополнить, как вы уже догадались, используя соответствующую заглавную букву вместо строчной. Итак, возвращаясь к примеру <p[^>]*> из предыдущего раздела, теперь вы можете видеть, что используемый им набор символов пытается соответствовать любому символу, кроме закрывающей угловой скобки.

Но если бы у меня было несколько переменных строк, которые я хотел бы сопоставить, все усложнялось?

Не бойся, жизнь всё ещё хороша и легка. Рассмотрим пример: в книге, которую вы конвертируете, на каждой нечетной странице написано «Title», а на каждой четной странице - «Author». Отлично смотрится в печати, верно? Но в электронных книгах это раздражает. Вы можете сгруппировать целые выражения в нормальных скобках, и символ "|" позволит вам сопоставить либо выражение справа, либо слева. Объедините их, и всё готово. Слишком быстро для тебя? Хорошо, во-первых, мы группируем выражения для нечетных и четных страниц, получая, таким образом, (Title)(Author) как два наших необходимых выражения. Теперь мы упростили ситуацию с помощью вертикальной черты ("|" называется символом вертикальной черты): если вы используете выражение (Title|Author), вы либо получите совпадение для «Title» (на нечетных страницах) или вы должны соответствовать «Author» (на четных страницах). Ну, разве не просто?

Конечно, вы можете использовать вертикальную черту, не используя групповые скобки. Помните, когда я говорил, что квантификаторы повторяют элемент, предшествующий им? Вертикальная черта работает немного по-другому: выражение «Title|Author» также будет соответствовать либо строке «Title», либо строке «Author», как в приведенном выше примере с использованием группировки. Вертикальная черта выбирает во всём выражении, предшествующее и следующее за ней. Итак, если вы хотите сопоставить строки «Calibre» и «calibre» и хотите выбирать только между прописными и строчными буквами «c», вам нужно использовать выражение (c|C)alibre, где группировка гарантирует, что будет выбрана только буква «с». Если бы вы использовали c|Calibre, вы бы получили совпадение в строке «c» или в строке «Calibre», что не то, что мы хотели. Вкратце: если есть сомнения, используйте группировку вместе с вертикальной чертой.

Вы пропустили…

… подождите минутку, есть еще одна действительно полезная вещь, которую вы можете сделать с группами. Если у вас есть группа, которую вы ранее сопоставляли, вы можете использовать ссылки на эту группу позже в выражении: Группы нумеруются, начиная с 1, и вы ссылаетесь на них, экранируя номер группы, на которую хотите сослаться, таким образом, пятая группа будет ссылаться как \5. Итак, если вы искали ([^]+)\1 в строке «Test Test», вы бы соответствовали всей строке!

В начале вы сказали, что есть способ сделать регулярное выражение нечувствительным к регистру?

Да, спасибо, что уделили мне внимание и напомнили. Вы можете сказать calibre, как вы хотите, чтобы определенные вещи обрабатывались с помощью чего-то, называемого флагами. Вы включаете флаги в свое выражение, используя специальную конструкцию (?flags go here), где, очевидно, вы бы заменили «flags go here» на конкретные флаги, которые вы хотите. Для игнорирования регистра флаг i, таким образом, вы включаете (?I) в свое выражение. Таким образом, (?I)test будет соответствовать «Test», «tEst», «TEst» и любым вариантам, которые вы можете придумать.

Другой полезный флаг позволяет точке соответствовать любому символу, * включая* перевод строки, флаг s. Если вы хотите использовать несколько выражений в выражении, просто поместите их в один и тот же оператор: (?is) проигнорирует регистр и сделает точку подходящей для всех. Неважно, какой флаг вы указали первым, (?si) будет эквивалентно приведенному выше.

Я думаю, что начинаю понимать эти регулярные выражения сейчас … как я могу использовать их в calibre?

Конвертации

Начнем с настроек конвертации, которые действительно аккуратны. В части Search & replace вы можете ввести регулярное выражение (сокращение от регулярного выражения), которое описывает строку, которая будет заменена во время преобразования. Аккуратная часть - это волшебник. Нажмите на посох волшебника, и вы получите предварительный просмотр того, что calibre «видит» в процессе конвертации. Прокрутите вниз до строки, которую хотите удалить, выберите и скопируйте её, вставьте в поле регулярных выражений в верхней части окна. Если есть переменные части, такие как номера страниц или вроде того, используйте наборы и квантификаторы, чтобы покрыть их, и пока вы это делаете, не забудьте экранировать специальные символы, если они есть. Нажмите кнопку с надписью Test, и calibre выделит части, которые он заменит, если вы будете использовать регулярное выражение. Как только вы удовлетворены, нажмите ОК и конвертируйте. Будьте осторожны, если в вашем источнике конвертацииии есть теги, подобные следующему:

Maybe, but the cops feel like you do, Anita. What's one more dead vampire?
New laws don't change that. </p>
<p class="calibre4"> <b class="calibre2">Generated by ABC Amber LIT Conv
<a href="http://www.processtext.com/abclit.html" class="calibre3">erter,
http://www.processtext.com/abclit.html</a></b></p>
<p class="calibre4"> It had only been two years since Addison v. Clark.
The court case gave us a revised version of what life was

(бессовестно вырванный из этой темы <https://www.mobileread.com/forums/showthread.php?t=75594 «>`_). Вам также придется удалить некоторые теги. В этом примере , я бы рекомендовал начать с тэга <b class="calibre2">, теперь вы должны закончить соответствующим закрывающим тэгом (открывающие тэги - <tag>, закрывающие тэги - </tag>), который просто является следующим </b> в этом случае. (Обратитесь к хорошему руководству по HTML или спросите на форуме, если вы не уверены в этом.) Открывающий тег можно описать с помощью``<b.*?>``, закрывающий тег с помощью </b>, таким образом, мы могли бы удалить все между этими тегами, используя <b. *?>.*?</b>. Но использование этого выражения было бы плохой идеей, потому что оно удаляет всё, что заключено в <b> -теги - (что, кстати, рендерит вложенный текст жирным шрифтом), и будет справедливо поспорить, что мы удалим части книги таким образом. Вместо этого включите также начало вложенной строки, создав регулярное выражение <b.*?>\s*Generated\s+by\s+ABC\s+Amber\s+LIT.*?</b>. \s с квантификаторами находятся в здесь вместо того, чтобы явно использовать пробелы, как видно в строке, чтобы поймать любые варианты строки, которые могут возникнуть. Не забудьте проверить, какой из них calibre удалит, чтобы убедиться, что вы не удаляете те части, которые хотите сохранить, если тестируете новое выражение. Если вы проверите только одно вхождение, вы можете пропустить несоответствие где-то ещё в тексте. Также обратите внимание, что, если вы случайно удалите больше или меньше тегов, чем вы на самом деле хотели, calibre попытается восстановить повреждённый код после удаления.

Добавление книг

Другая вещь, для которой вы можете использовать регулярные выражения, - это извлечение метаданных из имён файлов. Вы можете найти эту функцию в разделе «Добавление книг» в настройках. Здесь есть особенность: вы можете использовать имена полей для полей метаданных, например, (?P<title>) будет означать, что calibre использует эту часть строки в качестве названия книги. Разрешенные имена полей перечислены в окнах вместе с другим хорошим тестовым полем. Пример: скажем, вы хотите импортировать целую кучу файлов с такими именами, как Classical Texts: The Divine Comedy by Dante Alighieri.mobi (Классические тексты: Божественная комедия Данте Алигьери.mobi). (Очевидно, это уже есть в вашей библиотеке, так как мы все любим классическую итальянскую поэзию) или Science Fiction epics: The Foundation Trilogy by Isaac Asimov.epub (Фантастические эпопеи: трилогия Фонда» Исаака Азимова.epub). Это, очевидно, схема именования, из которой calibre не будет извлекать какие-либо значимые данные - стандартное выражение для извлечения метаданных - (?P<title>.+)-(?P<author>[^_]+) ``. Регулярное выражение, которое работает здесь, будет ``[a-zA-Z]+:(?P<title>. +) by (?P<author>.+). Обратите внимание, что внутри группы для поля метаданных вам нужно использовать выражения для описания того, что поле действительно соответствует. Также обратите внимание, что при использовании тестового поля calibre вам необходимо добавить расширение файла к вашему тестовому имени файла, иначе вы не получите никаких совпадений, несмотря на использование рабочего выражения.

Массовое редактирование метаданных

Последняя часть - поиск и замена регулярных выражений в полях метаданных. Вы можете получить доступ к этому, выбрав несколько книг в библиотеке и используя массовое редактирование метаданных. Будьте очень осторожны при использовании этой последней функции, поскольку она может сделать Очень плохие вещи для вашей библиотеки! Дважды проверьте, что ваши выражения делают то, что вы хотите, используя тестовые поля, и отмечайте только те книги, которые вы действительно хотите изменить! В режиме поиска по регулярному выражению вы можете искать в одном поле, заменять текст чем-либо и даже записывать результат в другое поле. Практический пример: скажем, ваша библиотека содержала книги из серии Фрэнка Герберта «Дюны», названные после моды Dune 1 - Dune, Dune 2 - Dune Messiah и так далее. Теперь вы хотите получить Dune в поле серии. Вы можете сделать это, выполнив поиск (.*?) \d+ - .* в поле заголовка и заменив его на \1 в поле серии. Видишь, что я там сделал? Это ссылка на первую группу, которой вы заменяете поле серии. Теперь, когда у вас есть все серии, вам нужно только выполнить другой поиск .*?- в поле заголовка и замените его на "" (пустую строку), снова в поле заголовка, и ваши метаданные будут аккуратными и чистыми. Разве это не здорово? Кстати, вместо замены всего поля вы также можете добавить или добавить к нему поле, так что, если вы хотите добавить к названию книги информацию о серии, вы также можете это сделать. Как вы, несомненно, уже заметили, есть флажок с надписью Чувствительный к регистру, поэтому вам не придется использовать флаги для выбора поведения здесь.

Ну, вот и заканчивается очень краткое введение в регулярные выражения. Надеюсь, я покажу вам достаточно, чтобы, по крайней мере, начать работу и продолжить самостоятельное обучение - хорошей отправной точкой будет документация Python для регулярных выражений <https://docs.python.org/library /re.html>`_.

Последнее слово предостережения: регулярные выражения мощная вещь, но в них также легко ошибиться. calibre предоставляет отличные возможности тестирования, чтобы увидеть, ведут ли себя ваши выражения так, как вы ожидаете. Используйте их. Старайтесь не стрелять себе в ногу. (Боже, мне нравится это выражение …) Но если вы, несмотря на предупреждение, пораните ногу (или любые другие части тела), постарайтесь извлечь из этого уроки.

Краткая справка

Признательность

Спасибо за помощь с советами, исправлениями и всем таким:

  • ldolse

  • ovidgoyal

  • chaley

  • dwanthny

  • kacir

  • Starson17

  • Orpheu

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