У меня есть такой txt-файл:
ххх.prontube.ru
salo.ru
bbb.antichat.ru
yyy.ru
xx.bb.prontube.ru
zzz.com
srfsf.jwbefw.com.ua
Пытаюсь удалить все поддомены с таким regex:
Find: .+\.((.*?)\.(ru|ua|com\.ua|com|net|info))$
Replace with: \1
Получаем:
prontube.ru
salo.ru
antichat.ru
yyy.ru
prontube.ru
zzz.com
com.ua
Почему последняя строка становится com.ua
вместо jwbefw.com.ua
?
Это работает без оглядки:
Find: [a-zA-Z0-9-.]+\.([a-zA-Z0-9-]+)\.([a-zA-Z0-9-]+)$
Заменить: \1\.\2
Он находит что-то, содержащее как минимум 2 периода и только буквы, цифры и тире, следующие за двумя последними периодами; затем он заменяет это на две последние части. На мой взгляд, это более интуитивно понятно.
С этим ведущим xxx
происходит что-то странное. Похоже, что это не обычный ASCII. В рамках данного вопроса я предположу, что это просто что-то забавное с этим сайтом и не является репрезентативным для ваших реальных данных.
Интересно, что ранее у меня был неправильный ответ, набравший много голосов. Поэтому я думаю, что мне стоит его сохранить:
Find: [a-zA-Z0-9-]+\.([a-zA-Z0-9-]+)\.(.+)$
Заменить: \1\.\2
Он просто находит имя хоста, в котором есть хотя бы 2 точки, а затем заменяет его на все, что находится после первой точки.
Часть .+
соответствует максимально возможному количеству. Попробуйте использовать .+?
вместо этого, и он будет захватывать как можно меньше, позволяя опции com.ua
соответствовать.
.+?\.([\w-]*?\.(?:ru|ua|com\.ua|com|net|info))$
В этом ответе по-прежнему используются конкретные доменные имена, о которых шла речь в исходном вопросе. Поскольку некоторые TLD (домены верхнего уровня) содержат точку, и теоретически у вас может быть список, включающий несколько поддоменов, белый список TLD в regex - хорошая идея, если он работает с вашим набором данных. Оба текущих ответа (от 2013 года) не будут корректно обрабатывать разницу между "xx.bb.prontube.ru" и "srfsf.jwbefw.com.ua".
Вот краткое объяснение того, почему оригинальный регекс в psnig' не работает так, как нужно:
+
жадный.
.+
будет застегиваться до самого конца строки справа, захватывая все,
а затем проделает обратный путь (влево), ища совпадения отсюда:
(ru|ua|com\.ua|com|net|info)
.
В случае srfsf.jwbefw.com.ua механизм regex сначала не найдет совпадения с a
,
затем он переместит маркер на одно место влево, чтобы посмотреть на "ua"
В этот момент ua
из regex (второй вариант) будет соответствовать.
Движок не будет продолжать искать "com.ua", потому что ".ua" удовлетворяет этому требованию.
Ответ Niet the Dark Absol' говорит регексу быть "ленивым";
.+?
будет соответствовать любому символу (хотя бы одному), а затем попытается найти следующую часть регекса. Если это не удается, он продвигает маркер вперед, .+
сопоставляет еще один символ, а затем снова оценивает остальную часть регекса.
В итоге .+? srfsf.jwbefw до совпадения с точкой, а затем совпадет с com.ua.
Но импликация ?
также создает проблемы.
Добавление вопросительного знака делает первый .+ ленивым, но затем заставляет группу1 соответствовать bb.prontube.ru вместо prontube.ru.
Это происходит потому, что первая точка после bb будет соответствовать, затем внутри группы 1 (.*?)
будет соответствовать bb.prontube. перед \.(ru|ua|com\.ua|com|net|info))$
соответствует .ru.
Чтобы избежать этого, измените третью группу с (.*?)
на ([\w-]*?)
, чтобы она не захватывала . только буквы и цифры, или тире.
Результирующий regex: *`.+?.(([\w-])?.(ru|ua|com.ua|com|net|info))$`**
Обратите внимание, что вам не нужно захватывать никакие группы, кроме первой. Добавление ?: делает опции TLD неперехватываемыми.
Последнее изменение: *`.+?.([\w-]?.(?:ru|ua|com.ua|com|net|info))$`**