LINUX.ORG.RU
ФорумTalks

Слава человечеству! Человекочитаемые regexp`ы...


1

1

Некоторое время назад мне надо было для одного наколенного проекта написать парсер и я в очередной раз столкнулся с регулярными выражениями.
Потом в распаршиваемых данных что-то изменилось и надо было переделать регулярки но на этом мой энтузиазм закончился.
Для меня регулярки остались writeonly хренью, которую при изменении проще переписать, чем что-то там править.
Тем более, что мне приходится изучать их заново каждый раз перед использованием. Эта дрянь не держится у меня в голове дольше 2-3 дней.
Мне кажется я такой не один.
А давайте сделаем, пусть и более многословные, но понятные для неподготовленного человека регулярные выражения?
Например надо выколупать из

<a href="example.com">link</a>
домен и название. Пусть это выглядит как-то так:
{
  skip_until("\"");
  cut_out(LITERALS);
  skip_until(">");
  cut_out(LETTERS);
}
Добавить разных skip_while, skip_until и т.п.
А чтобы не изобретать велосипед с нуля просто транслировать этот код к классические регулярки.
Есть тут спецы по регулярным выражениям, которые могут сказать что-то кроме «Не нужно, нас и так всё устраивает»?

★★☆
Ответ на: комментарий от Xellos

И тут выяснится, что его парсер время от времени затыкается в очень странных местах )) Потому что «zero or more times» можно записть и как «X*», и как «X*?», и как «X*+», и на неправильном употреблении парсер подвиснет ) Тогда автору придется ввести ключевые слова типа ZERO_OR_MORE_TIMES(GREEDY). Потом нужно будет написать комбинаторы стратегий... Итп...

stevejobs ★★★★☆
()

Для меня регулярки остались writeonly хренью, которую при изменении проще переписать, чем что-то там править.

Хех, аналогично :)

drull ★☆☆☆
()
Ответ на: комментарий от Stahl

Ты действительно совершенно не понимаешь сути регулярных выражений. Попробуй почитать Фридла, вдруг поможет.

/close thread

Xellos ★★★★★
()
Ответ на: комментарий от Stahl

Имхо, регулярки - это такие декларативные выражения, которые ВМЕСТО тебя пишут тот код, который ты предлагаешь писать руками в оп-посте. Сделаны как раз потому, что такой код писать адски сложно. Что-то вроде аналога SQL - в SQL ты тоже пишешь только то, какие данные хочешь получить, а конкретный код для этого за тебя придумывает БД. SQL писать легко, а конкретный код - сложно. Странно было бы предлагать вместо SQL писать какие-то циклы.

Наверное, тебе просто не нужны регулярки

stevejobs ★★★★☆
()
Ответ на: комментарий от dmfd

Как сделать без комбинаторов, например, альтернативы? В регулярках ЕМНИП они есть, значит и транслировать их нужно в комбинаторы. К примеру в spirit.

if-else, switch. Зачем что-то куда-то транслировать? Мое сообщение было о другом, я еще раз повторяю: если ТС хочет «развернуто» описывать правила обработки текста как в его примере, то он может это делать прямо в своем языке программирования, благо библиотечных функций обработки строк обычно везде хватает.

Мне что-то кажется, что область применения одного — подмножество другого.

Нет. Регэкспы обрабатывают текст, парсеры — лексемы. Причем в регэкспах все смешалось, кони, люди, лексика, синтаксис и даже семантика (правила модификации текста).

staseg ★★★★★
()
Ответ на: комментарий от vsemnazlo

Нафига тогда такие сложности, которые предложил ТС, чтобы распарсить один тег?
Это был лишь пример.

kovrik ★★★★★
()

Если напишешь транслятор традиционных регэкспов в свои и из своих в традиционные, буду пользоваться.

CYB3R ★★★★★
()
Ответ на: комментарий от staseg

if-else, switch

И код с аккуртаной передачей/возвратом итераторов куда нужно, да. Это не альтернатива регуляркам получится (которая должна быть декларативной), а что-то вроде куцой реализации их интерпретатора.

Регэкспы обрабатывают текст, парсеры — лексемы.

Может быть, я не программист и в терминологии не силён.

dmfd
()

Дай больше ада в этот тред. Как ты предлагаешь указывать ленивые квантификаторы?

Deleted
()
Ответ на: комментарий от kovrik

Это уж другое дело.

Кстати, для пущей понятности и удобства можно выражения и частями собирать, типа:

RX_OCTET='(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)'
RX_IPv4="\b$RX_OCTET\.$RX_OCTET\.$RX_OCTET\.$RX_OCTET\b"
vsemnazlo
()
Ответ на: комментарий от dmfd

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

Конечно. Но именно к этому пока что и идет ТС. Либо у него получится еще один C++/Python, либо ОЧЕНЬ многословная штука, либо, в попытке укоротить второе, еще один непонятный регэксп.

staseg ★★★★★
()
Ответ на: комментарий от CYB3R

а написать 100500 конструкций, все их запомнить, и транслировать регулярки туда-сюда - удобнее? сомневаюсь. опять же, если первоначальная проблема в том, что регулярные выражения для тебя - read-only, и проше переписать, нежели модифицировать, выхода (очевидных) два - учить матчасть и использовать комментарии.

George
()

ТС просто неосилил регекспы.

</thread>

Chaser_Andrey ★★★★★
()

{
skip_until(«\»");
cut_out(LITERALS);
skip_until(«>»);
cut_out(LETTERS);
}

a) http://xkcd.com/927/
b) Проходит два года и у вас внезапно новый xml, но регекспы.

takino ★★★★★
()
Ответ на: комментарий от CYB3R

напротив. сделал бы все еще более запутанным.

George
()

Поздравляю, вы переизобрели Snobol.

buddhist ★★★★★
()
Ответ на: комментарий от staseg

Но именно к этому пока что и идет ТС.

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

tag = do
    char '<'
    tagName <- many alphaNum
    char '>'
    return tagName
dmfd
()

Я уже представляю, как здесь будет выглядеть валидация имейла
http://www.ex-parrot.com/pdw/Mail-RFC822-Address.html

(?:(?:\r\n)?[ \t])*(?:(?:(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t]
)+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:
\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(
?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ 
\t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\0
31]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\
](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+
(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:
(?:\r\n)?[ \t])*))*|(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z
|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)
?[ \t])*)*\<(?:(?:\r\n)?[ \t])*(?:@(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\
r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[
 \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)
?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t]
)*))*(?:,@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[
 \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*
)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t]
)+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*)
*:(?:(?:\r\n)?[ \t])*)?(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+
|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r
\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t
]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031
]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](
?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?
:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?
:\r\n)?[ \t])*))*\>(?:(?:\r\n)?[ \t])*)|(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?
:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?
[ \t]))*"(?:(?:\r\n)?[ \t])*)*:(?:(?:\r\n)?[ \t])*(?:(?:(?:[^()<>@,;:\\".\[\] 
\000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|
\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>
@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"
(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t]
)*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\
".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?
:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[
\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*|(?:[^()<>@,;:\\".\[\] \000-
\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(
?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)*\<(?:(?:\r\n)?[ \t])*(?:@(?:[^()<>@,;
:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([
^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\"
.\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\
]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*(?:,@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\
[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\
r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] 
\000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]
|\\.)*\](?:(?:\r\n)?[ \t])*))*)*:(?:(?:\r\n)?[ \t])*)?(?:[^()<>@,;:\\".\[\] \0
00-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\
.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,
;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?
:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*
(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".
\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[
^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]
]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*\>(?:(?:\r\n)?[ \t])*)(?:,\s*(
?:(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\
".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(
?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[
\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t
])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t
])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?
:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|
\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*|(?:
[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\
]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)*\<(?:(?:\r\n)
?[ \t])*(?:@(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["
()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)
?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>
@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*(?:,@(?:(?:\r\n)?[
 \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,
;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t]
)*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\
".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*)*:(?:(?:\r\n)?[ \t])*)?
(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".
\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:
\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\[
"()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])
*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])
+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\
.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z
|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*\>(?:(
?:\r\n)?[ \t])*))*)?;\s*)

iVS ★★★★★
()
Ответ на: комментарий от quantum-troll

Пробеги по дереву, пробеги по дереву, пробеги по дереву ещё раз.

Разве все xml-парсеры такие?

ados ★★★★★
()
Ответ на: комментарий от ados

Многие таки да, правда там есть css селекторы/xpath, что пробегут часть дерева за тебя.
А ты знаешь действительно простые в использовании парсеры иксэмэлей и html'а?

quantum-troll ★★★★★
()
Ответ на: комментарий от Absurd

//тред не четал

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

Stahl ★★☆
() автор топика
Ответ на: комментарий от stevejobs

Вменяемый ответ на первой же странице?!
Что с ЛОРом весна делает.

WatchCat ★★★★★
()

Есть тут спецы по регулярным выражениям, которые могут сказать что-то кроме «Не нужно, нас и так всё устраивает»?

не нужно потому, что писать многобукв устанешь.

Тем более, что мне приходится изучать их заново каждый раз перед использованием. Эта дрянь не держится у меня в голове дольше 2-3 дней.

странно. Для меня это как велосипед: один раз научился, и на всю жизнь. Я сам даже не понимаю, как оно работает(раньше понимал), а тупо беру и пишу.

drBatty ★★
()
Ответ на: комментарий от dmfd

На C++ нельзя.

просто потому, что есть REGEX(3) (в glibc). А у кого не GNU (а какой-нить микрософт), тому регекспы не понять из-за врождённой тупости.

drBatty ★★
()
Ответ на: комментарий от CARS

на самом удобном и человечном языке, то есть на русском.

ИДИ_НА
drBatty ★★
()

Пусть это выглядит как-то так:
{
skip_until(«\»");
cut_out(LITERALS);
skip_until(«>»);
cut_out(LETTERS);
}



Молодец, черновик статического API нескучных регэкспов есть. Теперь пора открыть тайну: регэкспы реализуются через конечные автоматы, имеющие СОСТОЯНИЕ.

shahid ★★★★★
()
Ответ на: комментарий от Stahl

И не пытайся убедить меня, что просто опечатался.

Нет я специально. У меня и носки всегда разного цвета - я так вычисляю унылых зануд в своем окружении.

Absurd ★★★
()
Ответ на: комментарий от Absurd

Гм. Не очень хороший детектор.
Я не очень-то уныл, но придраться к какой-то мелочи иногда люблю.
Знаешь, так, чтобы злость согнать. Нафиг не пошлёшь кого попало ибо я недостаточно квадратен, а по-мелочёвке кольнёшь и на душе веселее.

Stahl ★★☆
() автор топика
Ответ на: комментарий от Stahl

У тебя в примере кусок html'я который ты собрался регэкспами парсить. Для этого есть phantomjs, где можно без лишнего секса пройтись по DOM'у. Во всех остальных случаях - регулярки простое и понятное решение.

AiFiLTr0 ★★★★★
()

больше велосипедов !
хороших и не очень !

вот чем мне нравится опен-сорс - в нем нет ничего застывшего, все постоянно изменяется

kto_tama ★★★★★
()
Ответ на: комментарий от quantum-troll

А ты знаешь действительно простые в использовании парсеры иксэмэлей и html'а?

cl-libxml2, промышленно xml не парсю, а какие парсю, с ними твоя критика о вложенности деревьев не уместна (небольшая вложенность и объём). Поэтому считаю, что использование проще некуда.

ados ★★★★★
()

> Пусть это выглядит как-то так:

perl -Mre=debug -e 'q[<a href="example.com">link</a>] =~ /"([^"]+)"[^>]+>([^<]+)/'
arsi ★★★★★
()
Последнее исправление: arsi (всего исправлений: 1)

HTML надо парсить не регулярками, а библиотеками HTML DOM.

Даже в пыхе с его армией регуляркодрочеров и то такая есть.

reserved
()

Например надо выколупать из

<a href=«example.com»>link</a>

домен и название.

expat, кажется. Ну, или libxml. Быстро и решительно.

GateKeeper ★★
()

вообще-то есть либы, которые представят твою разметку как DOM дерево, и тебе останется только взять нужные атрибуты нужного item в дереве. я недавно делал такое на питоне через etree и xpath.

Komintern ★★★★★
()

Заковыка в том, что простые регекспы читаются не хуже, а для сложных будут такие простыни кода, что повесишься пока всё распарсишь. Многострочные регекспы с комментариями намного лучше.

nikodymus
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.