Игнорировать латексные макросы при замене символов на регулярное выражение - PullRequest
2 голосов
/ 07 апреля 2019

У меня есть файл, который мне нужно преобразовать из пользовательской кодовой страницы. В файле есть такие вещи:

foo bar baz \bazfoo \barfoo foo bar \foobar

Я хочу заменить foo на bar, кроме случаев, когда foo возникает как часть макроса LaTeX, такого как \ bazfoo, \ barfoo и \ foobar

Другими словами, s/foo/bar/, но \ bazfoo должен оставаться \ bazfoo. Есть ли способ сделать это с помощью операторов lookead?

Ответы [ 4 ]

4 голосов
/ 07 апреля 2019

Может потребоваться, чтобы слово с шаблоном не начиналось с \, используя отрицательный символьный класс

s{(?: ^|\s ) (?: [^\\\s]\S* )? \K foo}{XXX}gx

foo также может находиться в начале строки, или слово,таким образом, чередование ^|\s и [^\\\s]\S* является необязательным.\ необходимо экранировать в классе символов, иначе он сам избежит ].

. \K отбрасывает все совпадения до этой точки, поэтому нам не нужно захватывать их и помещатьих обратно.

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

Test, с добавлением к вашей тестовой строке

perl -wE'$_=q(foo bar somefoo \bazfoo \barfoo foo bar \foobar); say; 
    s{(?: ^|\s ) (?: [^\\\s]\S* )? \K foo}{XXX}gx; say'

печатает

foo bar somefoo \bazfoo \barfoo foo bar \foobar
XXX bar someXXX \bazfoo \barfoo XXX bar \foobar

Обратите внимание, что ваша тестовая строка не включает случай, когда fooвнутри слова, но все еще нуждается в замене, как somefoo.Я добавил это выше

2 голосов
/ 07 апреля 2019

Если вам просто нужно обработать один foo на слово:

s/ (?: ^ | \s++ ) (?: [^\\\s]\S* )? \K foo /bar/gx

Если вам просто нужно обработать несколько foo на слово:

s{ (?: ^ | \s++ ) \K ( [^\\\s]\S* ) }{ $1 =~ s/foo/bar/rg }egx

Это фиксированные и оптимизированные версии решений в предыдущих ответах. (Исправления, но не оптимизация, внесенная в более ранние ответы.)

2 голосов
/ 07 апреля 2019

Хотя zdim уже предлагает увлекательное решение, я все же хочу поделиться своей версией.

У меня также были проблемы из-за вида переменной длины.

Таким образом, мое решение состоит в том, чтобы «токенизировать» значение строки: выбрать каждое «слово» и заменить только те, которые не начинаются с \.

perl -e '
  $_=q(foo bar baz \bazfoo \barfoo foo bar \foobar);
  s/(\S+)/                  # pick the word
    $word=$1;               # save it
    if ($word!~m#^\\#) {    # test for LaTeX
      $word=~s#foo#bar#g;   # otherwise replace
    }
    $word                   # the result
  /gex;                     # globally, execute and eXtended for comments
  print $_;
'

К сожалению, для этого необходимо использовать флаг "e" (- xecute).

Обновление: Согласно @Alex (см. Комментарий ниже) »Это решение не найдет {\ foo}, который является допустимым синтаксисом LaTeX.«.

Поэтому, если необходимо, измените приведенную выше строку оператора if на if ($word!~m#^\\|^\{\\.*\}$#) {.

0 голосов
/ 10 апреля 2019

, если мы уверены, что любой символ латексного токена никогда не будет словом char. и ваши данные в 'd', просто:

sed -E 's/(^|\s)(\w*)foo/\1\2bar/g' d
perl -pe 's/(^|\s)(?:\w*)foo/$1bar/g' d
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...