Регулярные выражения — ликбез по регэкспам

Здесь обсуждаются темы, косвенно связанные с Far.
User avatar
Wave
Posts: 1721
Joined: Thu 04 Aug, 2005 18:47
Has thanked: 2 times
Been thanked: 10 times

Регулярные выражения — ликбез по регэкспам

Post by Wave » Mon 20 Feb, 2006 00:41

Очень хотелось бы больше примеров по регэкспам в документации, т.к. впервые столкнувшись с серьёзной задачей, разобраться очень трудно. Сколько ни бился, так и не получилось такое вот: имеется много html\xml файлов, в них теги прописаны большими, маленькими буквами и смешаным регистром. У тегов естессно, есть параметры, причём в виде: параметр=значение, параметр="значение", параметр='значение'. Требуется привести теги и их параметры, не имеющие смешаного регистра, к \l, при этом ни в коем случае не менять регистр значений. Более того, все значения нужно взять в кавычки.

Т.е. <A HREF=http://www.some.com/REG.HTML>...</A> нужно перевести в <a href="http://www.some.com/REG.HTML">...</a>
<SomeTag SomeParam=SomeValue /> перевести <SomeTag SomeParam="SomeValue" />,
а, допустим <meta http-equiv="Content-Type" content=text/html; charset=windows-1251> в <meta http-equiv="Content-Type" content="text/html;" charset="windows-1251">

Задача номер следующая: теги, не имеющие закрывающую пару, и не закрывающиеся сами - закрыть. Т.е. те же <meta http-equiv="Content-Type" content="text/html;" charset="windows-1251"> перевести в <meta http-equiv="Content-Type" content="text/html;" charset="windows-1251" />
<img src="..."> в <img src="..." />

Я так понимаю, что задача выполнимая. Но как это сделать? По существующей документации разобраться очень трудно.

<img src="..."> - это если (<.*?\s)(.*?(?<![\/|\/\s])>) и как-то обозначить, что дальше по тексту не встречается $1.*?(?<=[\/|\/\s])> - как-то так...
То тогда менять на $1...[внутренности тега]... /> Во всяком случае такая строка поиска находит теги, не закрывающие сами себя... И в то же время такие вот вещи "<b> some </b>some<b>" (оказывается выделенным всё, что в кавычках). Но как дальше? И как избежать вот этих ошибок. Нужно как-то обозначить, что внутри $1 не должно быть ">" и вообще та закрывающая скобка, которая явно нарисована, должна быть первой закрывающей.

Да, ещё, нужно учесть, что закрывающий тег может быть и не в той же самой строке. А в сложных случаях - вложенность тегов...

И ещё, можно ли как-то указать, что группа должна быть больше, меньше, равно, не больше, не меньше, чем столько-то символов? Не считая, конечно, явного составления из "ровно один любой символ". Могу я для строки AZXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX указав A.*X, указать ещё и длину не больше 15 символов? Хотя здесь ещё просто, а вот для предыдущих упражнений с тегами задавать длину не более 75 символов со всеми параметрами?

Как можно задать строку-исключение? "XXX" не должна входить в "A.*?Z".

Думаю, если подробный разбор этого дела сделать (с пояснениями), то практически с любой степенью сложности регэкспов можно будет справиться. Или есть и более сложные примеры?

(<(?!\/).+?\s(?<!>))(.*?(?<![\/|\/\s])>) находит "<b> some </b>", но не "<b> some </b>some<b>" (без кавычек).
А ещё "<pre><font size=4>".
(<(?!\/).+?[\s>](?<!>))(.*?(?<![\/|\/\s])>) и того хуже.
Пользуюсь ФАРом больше 15 лет © собирательный.

al666
Posts: 237
Joined: Mon 04 Jul, 2005 08:55

Post by al666 » Mon 20 Feb, 2006 08:28

Рекомендую не пытаться объять необъятное (или впихнуть невпихуемое - кому как больше нравится :).
Все перечисленное надо разбить на отдельные маленькие задачи.

Например:
1) исправление регистра в открывающих тегах
искать: <[A-Z ]+?=
заменить: \L$0

2) исправление регистра в закрывающих тегах
искать: <\/[A-Z]+?>
заменить: \L$0

3) замена одинарных кавычек на двойные:
искать: ='(\w+)'
заменить: ="$1"

и так далее...

P.S. Насчет ограничения длины для выражения <regexp> - попробуй (?=<regexp>)<.{1,75}>

User avatar
Centaur
Posts: 1083
Joined: Tue 05 Apr, 2005 20:09
Location: Novosibirsk, Russia

Post by Centaur » Mon 20 Feb, 2006 10:06

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

Вышеописанная задача больше похожа на приведение достандартного HTML к формату XHTML или XML. Её решает, например, программа HTML Tidy.

User avatar
Wave
Posts: 1721
Joined: Thu 04 Aug, 2005 18:47
Has thanked: 2 times
Been thanked: 10 times

Post by Wave » Mon 20 Feb, 2006 23:05

al666 - спасибо. Попробую от этого поплясать.
Centaur - посмотрю и прогу. Мне действительно часто приходится приводить
HTML к XHTML\XML. Но, во-первых: не только. Во-вторых: если это самый слабый
способ задания языков - то что сильней (из универсальных средств)?

К регэкспам я пришёл после программулины HTMLchanger (http://sura.ru/emanovs/)
- мощнейшая вещь для пакетной обработки файлов с довольно простым и слабоватым
синтаксисом. Однако с её помощью я, к примеру, поубирал банеры из сотен
мегабайт документации. Там можно задавать сразу много шаблонов\схем сразу, чего
нельзя в s_and_r\REsearch. А ещё там есть не только подтверждение, но и
возможность редактировать на лету результат замены - в диалоге подтверждения
это предусмотрено. Ну и ещё вкусности.

HTMLchanger'а перестало хватать для моих задач, зато открыл для себя регэкспы.
Которые посложней будут. Вот и хочу разобраться с ними. А проще всего это
сделать на примерах, которые хотелось бы иметь сразу в стандартной поставке.
Готов даже поделиться своим опытом.

Здесь ещё одна задача: многие плугины основаны как раз на сабже. К примеру
TrueTemplate. Не умея читать этот синтаксис хотя бы на уровне любимого языка
программирования, разобраться с плугином сложно - проще снести и забыть.
Пользуюсь ФАРом больше 15 лет © собирательный.

User avatar
Px
Posts: 826
Joined: Fri 08 Sep, 2006 15:52
Location: Вишнёвый (под Киевом)
Contact:

Re: Far 2.0 - regexp

Post by Px » Sun 24 Jan, 2010 16:01

Небольшой вопрос: хочу убрать в файле \N между словами, но оставить там, где он есть после точки или другого знака препинания
Пример:
всегда\Nмогу
сегодня\Nне
regexp для поиска сделал такой

Code: Select all

(\l)\\N(\l)
а вот что подставить в диалог замены, чтобы \N заменилось на пробел, а соседние символы остались неизменными? :oops:

Shmuel
Posts: 5230
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 16 times
Been thanked: 151 times

Re: Far 2.0 - regexp

Post by Shmuel » Sun 24 Jan, 2010 16:14

$1 $2

User avatar
Px
Posts: 826
Joined: Fri 08 Sep, 2006 15:52
Location: Вишнёвый (под Киевом)
Contact:

Re: Far 2.0 - regexp

Post by Px » Sun 24 Jan, 2010 16:23

Спасибо!

User avatar
WARP ItSelf
Самый умный
Posts: 1011
Joined: Tue 15 Mar, 2005 23:19
Contact:

Помогите с регулярным выражением.

Post by WARP ItSelf » Wed 30 Jun, 2010 12:56

Задача такая, есть строка с версией (сами знаете чего :P ). Строка имеет довольно произвольный вид, известно о ней только то, что она содержит числа, точки и буквы. Подходящие строки:

1.0 alpha 1
2.0.0rc2
1.0.0a
1.0.0.5

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

User avatar
HaRT
Moderator
Posts: 8116
Joined: Tue 30 Aug, 2005 17:21
Location: Санкт-Петербург
Has thanked: 67 times
Been thanked: 104 times

Re: Помогите с регулярным выражением.

Post by HaRT » Wed 30 Jun, 2010 13:28

В предположении, что строка начинается с цифры, правильно ли я понял, что строка имеет такой вид:
ЦЦ1 ТП БП1 ТП ЦЦ2 ТП БП2 ТП ЦЦ3 ТП БП3 ТП ЦЦ4 ТП БП4,
где
ЦЦ# — цифры
БП# — буквы и подчерки
ТП — точки и пробелы
Фар есть инструмент, а не нянька. © 2009 DrKnS

Shmuel
Posts: 5230
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 16 times
Been thanked: 151 times

Re: Помогите с регулярным выражением.

Post by Shmuel » Wed 30 Jun, 2010 14:00

Code: Select all

(\d+)[\. ]?(\D*)(\d*)[\. ]?(\D*)(\d*)[\. ]?(\D*)(\d*)[\. ]?(\D*)
$1, $3, $5, $7 будут содержать числа (или пустые строки, если числа нет)
$2, $4, $6, $8 - аналогично для "огрызков строк", одиночные точки и пробелы - пропускаются.

darkmist
Posts: 124
Joined: Wed 08 Aug, 2007 19:44

Re: Помогите с регулярным выражением.

Post by darkmist » Wed 30 Jun, 2010 14:03

cat <<EOL | perl -MData::Dumper -ne"my @a = grep { not /^(\s+|\.|)$/ } split /(\s+|\.|\d+|\a+)/; print Dumper( \@a )"
1.0 alpha 1
2.0.0rc2
1.0.0a
1.0.0.5
EOL

dmk
Posts: 32
Joined: Tue 26 Feb, 2008 10:01

Re: Помогите с регулярным выражением.

Post by dmk » Wed 30 Jun, 2010 14:14

на питоне:
ss = ("1.0 alpha 1",
"2.0.0rc2",
"1.0.0a",
"1.0.0.5"
)

import re

for s in ss:
m = re.match(r'(\d+)(\.(\d+))?(\.(\d+))?(\.(\d+))?(.*)', s)
if m:
print m.group(1, 3, 5, 7, 8)
-----------------------
('1', '0', None, None, ' alpha 1')
('2', '0', '0', None, 'rc2')
('1', '0', '0', None, 'a')
('1', '0', '0', '5', '')

User avatar
WARP ItSelf
Самый умный
Posts: 1011
Joined: Tue 15 Mar, 2005 23:19
Contact:

Re: Помогите с регулярным выражением.

Post by WARP ItSelf » Wed 30 Jun, 2010 14:29

Shmuel

Взлетело, похоже. Спасибо.

Всем остальным тоже спасибо, временно отбой, пока работает.
Никто не знал, а я...

AGu
Posts: 111
Joined: Wed 16 Mar, 2005 13:17
Location: Novosibirsk
Contact:

Re: Помогите с регулярным выражением.

Post by AGu » Wed 30 Jun, 2010 15:12

На JScript

Code: Select all

var TestStrings = [
  '1.0 alpha 1',
  ' 2.0.0rc2 ',
  '1.0.0a',
  '1.0 RC 2 10 beta 5'
];

var NumbersRE = /\d+/g;
var OthersRE  = /[^\d\.\s][^\d\.]*?(?=[\d\.\s]|$)/g;

function show(a){
  if(a==null || a.length==0) return ' not found';
  var result = '';
  for(var n=0; n<a.length; ++n) result += ' "'+a[n]+'"';
  return result;
}

for(var i=0; i<TestStrings.length; ++i){
  var s = TestStrings[i];
  WScript.echo(
    '"'+s+'"\r\n'+
    '\r\n'+
    '  Numbers:'+show(s.match(NumbersRE))+'\r\n'+
    '  Others:' +show(s.match(OthersRE)) +'\r\n');
}

Max Rusov
Posts: 3600
Joined: Wed 23 Nov, 2005 11:11
Location: Москва-Питер
Been thanked: 7 times

Re: Помогите с регулярным выражением.

Post by Max Rusov » Wed 30 Jun, 2010 15:18

WARP ItSelf wrote:Взлетело, похоже.
Ну когда же?...

Demiurg
Posts: 22
Joined: Wed 05 May, 2010 07:39
Location: Химки
Contact:

Re: Помогите с регулярным выражением.

Post by Demiurg » Mon 05 Jul, 2010 13:42

Perl

Code: Select all

my @a = ( '1.0 alpha 1', '2.0.0rc2', '1.0.0a', '1.0.0.5' );

for( @a ) {
    my @b = split /\W|(?<=\d)(?=\D)/;
    print join(', ', @b) . "\n";
}
Результат:

Code: Select all

1, 0, alpha, 1
2, 0, 0, rc2
1, 0, 0, a
1, 0, 0, 5

Post Reply

Return to “Операционные системы, командные оболочки и прочее”