В текстовом редакторе я хочу заменить данное слово номером строки, на котором это слово найдено. Возможно ли это с помощью Regex?
Может ли Regex возвращать номер строки, в которой найдено совпадение?
Ответ 1
Рекурсия, группа самореференций (трюк Qtax), обратные Qtax или балансировочные группы
Введение
Идея добавления списка целых чисел в нижней части ввода аналогична известному взлому базы данных (не имеет никакого отношения к регулярному выражению), где вы присоединяетесь к таблице целых чисел. Мой оригинальный ответ использовал трюк @Qtax. В текущих ответах используются либо рекурсия, трюк Qtax (прямой, либо обратный вариант), либо балансировочные группы.
Да, это возможно... С некоторыми оговорками и обманом регулярного выражения.
- Решения в этом ответе означают как средство демонстрации синтаксиса регулярного выражения, а не практические ответы, которые должны быть реализованы.
-  В конце вашего файла мы вставим список номеров, которым предшествует уникальный разделитель. Для этого эксперимента добавленная строка :1:2:3:4:5:6:7Это аналогичный метод известного хакера базы данных, который использует таблицу целых чисел.
- Для первых двух решений нам нужен редактор, который использует аромат регулярного выражения, который позволяет рекурсию (решение 1) или группы саморегуляции захвата (решения 2 и 3). Два приходят на ум: Notepad ++ и EditPad Pro. Для третьего решения нам нужен редактор, который поддерживает балансировочные группы. Это, вероятно, ограничивает нас EditPad Pro или Visual Studio 2013 +.
Входной файл:
Скажем, мы ищем pig и хотим заменить его номером строки.
Мы будем использовать это как ввод:
my cat
dog
my pig
my cow
my mouse
:1:2:3:4:5:6:7
Первое решение: рекурсия
Поддерживаемые языки: помимо текстовых редакторов, упомянутых выше (Notepad ++ и EditPad Pro), это решение должно работать на языках, использующих PCRE (PHP, R, Delphi), в Perl и на Python с использованием модуля Matthew Barnett regex (непроверенные).
Рекурсивная структура живет в представлении и не является обязательной. Его задача состоит в том, чтобы сбалансировать строки, которые не содержат pig, слева, с цифрами справа: подумайте об этом как о балансировке вложенной конструкции, например {{{ }}}... За исключением того, что слева у нас нет -match lines, а справа мы имеем числа. Дело в том, что когда мы выходим из lookahead, мы знаем, сколько строк было пропущено.
Поиск:
(?sm)(?=.*?pig)(?=((?:^(?:(?!pig)[^\r\n])*(?:\r?\n))(?:(?1)|[^:]+)(:\d+))?).*?\Kpig(?=.*?(?(2)\2):(\d+))
Версия свободного пробела с комментариями:
(?xsm)             # free-spacing mode, multi-line
(?=.*?pig)        # fail right away if pig isn't there
(?=               # The Recursive Structure Lives In This Lookahead
(                 # Group 1
   (?:               # skip one line 
      ^              
      (?:(?!pig)[^\r\n])*  # zero or more chars not followed by pig
      (?:\r?\n)      # newline chars
    ) 
    (?:(?1)|[^:]+)   # recurse Group 1 OR match all chars that are not a :
    (:\d+)           # match digits
)?                 # End Group 
)                 # End lookahead. 
.*?\Kpig                # get to pig
(?=.*?(?(2)\2):(\d+))   # Lookahead: capture the next digits
  Заменить: \3
В демо, см. подстановки внизу. Вы можете играть с буквами в первых двух строках (удалить пробел, чтобы сделать pig), чтобы перенести первое вхождение pig в другую строку и посмотреть, как это влияет на результаты.
Второе решение: группа, которая относится к самому себе ( "Qtax Trick" )
Поддерживаемые языки: помимо текстовых редакторов, упомянутых выше (Notepad ++ и EditPad Pro), это решение должно работать на языках, использующих PCRE (PHP, R, Delphi), в Perl и на Python с использованием модуля Matthew Barnett regex (непроверенные). Решение легко адаптируется к .NET, преобразовывая \K в lookahead и притяжательный квантификатор в атомную группу (см..NET Version несколько строк ниже.)
Поиск:
(?sm)(?=.*?pig)(?:(?:^(?:(?!pig)[^\r\n])*(?:\r?\n))(?=[^:]+((?(1)\1):\d+)))*+.*?\Kpig(?=[^:]+(?(1)\1):(\d+))
Версия .NET: Назад в будущее
.NET не имеет \K. Это его место, мы используем "назад к будущему" lookbehind (lookbehind, который содержит взгляд, который пропускает перед матчем). Кроме того, нам нужно использовать атомную группу вместо притяжательного квантификатора.
(?sm)(?<=(?=.*?pig)(?=(?>(?:^(?:(?!pig)[^\r\n])*(?:\r?\n))(?=[^:]+((?(1)\1):\d+)))*).*)pig(?=[^:]+(?(1)\1):(\d+))
Версия свободного пробела с комментариями (версия Perl/PCRE):
(?xsm)             # free-spacing mode, multi-line
(?=.*?pig)        # lookahead: if pig is not there, fail right away to save the effort
(?:               # start counter-line-skipper (lines that don't include pig)
   (?:               # skip one line 
      ^              # 
      (?:(?!pig)[^\r\n])*  # zero or more chars not followed by pig
      (?:\r?\n)      # newline chars
    )   
   # for each line skipped, let Group 1 match an ever increasing portion of the numbers string at the bottom
   (?=             # lookahead
      [^:]+           # skip all chars that are not colons
      (               # start Group 1
        (?(1)\1)      # match Group 1 if set
        :\d+          # match a colon and some digits
      )               # end Group 1
   )               # end lookahead
)*+               # end counter-line-skipper: zero or more times
.*?               # match
\K                # drop everything we've matched so far
pig               # match pig (this is the match!)
(?=[^:]+(?(1)\1):(\d+))   # capture the next number to Group 2
Заменить:
\2
Выход:
my cat
dog
my 3
my cow
my mouse
:1:2:3:4:5:6:7
В демо, см. подстановки внизу. Вы можете играть с буквами в первых двух строках (удалить пробел, чтобы сделать pig), чтобы перенести первое вхождение pig в другую строку и посмотреть, как это влияет на результаты.
Выбор разделителя для цифр
В нашем примере разделитель : для строки цифр довольно распространен и может произойти в другом месте. Мы можем придумать UNIQUE_DELIMITER и слегка подстроить выражение. Но следующая оптимизация еще более эффективна и позволяет нам хранить :
Оптимизация для второго решения: обратная строка цифр
Вместо того, чтобы вставлять наши цифры в порядок, нам может быть полезно использовать их в обратном порядке: :7:6:5:4:3:2:1
В наших взглядах это позволяет нам спуститься к нижней части ввода с помощью простого .* и начать отступать оттуда. Поскольку мы знаем, что мы находимся в конце строки, нам не нужно беспокоиться о том, что :digits является частью другого раздела строки. Вот как это сделать.
Ввод:
my cat pi g
dog p ig
my pig
my cow
my mouse
:7:6:5:4:3:2:1
Поиск:
(?xsm)             # free-spacing mode, multi-line
(?=.*?pig)        # lookahead: if pig is not there, fail right away to save the effort
(?:               # start counter-line-skipper (lines that don't include pig)
   (?:               # skip one line that doesn't have pig
      ^              # 
      (?:(?!pig)[^\r\n])*  # zero or more chars not followed by pig
      (?:\r?\n)      # newline chars
    )   
   # Group 1 matches increasing portion of the numbers string at the bottom
   (?=             # lookahead
      .*           # get to the end of the input
      (               # start Group 1
        :\d+          # match a colon and some digits
        (?(1)\1)      # match Group 1 if set
      )               # end Group 1
   )               # end lookahead
)*+               # end counter-line-skipper: zero or more times
.*?               # match
\K                # drop match so far
pig               # match pig (this is the match!)
(?=.*(\d+)(?(1)\1))   # capture the next number to Group 2
  Заменить: \2
Смотрите подстановки в демо.
Третье решение: балансировочные группы
Это решение специфично для .NET.
Поиск:
(?m)(?<=\A(?<c>^(?:(?!pig)[^\r\n])*(?:\r?\n))*.*?)pig(?=[^:]+(?(c)(?<-c>:\d+)*):(\d+))
Версия свободного пробела с комментариями:
(?xm)                # free-spacing, multi-line
(?<=                 # lookbehind
   \A                # 
   (?<c>               # skip one line that doesn't have pig
                       # The length of Group c Captures will serve as a counter
     ^                    # beginning of line
     (?:(?!pig)[^\r\n])*  # zero or more chars not followed by pig
     (?:\r?\n)            # newline chars
   )                   # end skipper
   *                   # repeat skipper
   .*?                 # we're on the pig line: lazily match chars before pig
   )                # end lookbehind
pig                 # match pig: this is the match
(?=                 # lookahead
   [^:]+               # get to the digits
   (?(c)               # if Group c has been set
     (?<-c>:\d+)         # decrement c while we match a group of digits
     *                   # repeat: this will only repeat as long as the length of Group c captures > 0 
   )                   # end if Group c has been set
   :(\d+)              # Match the next digit group, capture the digits
)                    # end lokahead
  Заменить: $1
Ссылка
Ответ 2
Поскольку вы не указали, какой текстовый редактор, в vim это будет:
 :%s/searched_word/\=printf('%-4d', line('.'))/g (подробнее)
Но как кто-то упомянул об этом не вопрос для SO, а скорее Super User;)
Ответ 3
Я не знаю редактора, который не ограничивается расширением редактора, который позволяет произвольные расширения.
Вы можете легко использовать perl для выполнения задачи.
perl -i.bak -e"s/word/$./eg" file
Или, если вы хотите использовать подстановочные знаки,
perl -MFile::DosGlob=glob -i.bak -e"BEGIN { @ARGV = map glob($_), @ARGV } s/word/$./eg" *.txt
