Вопрос или проблема
Типичная проблема в LaTeX:
\SomeStyle{\otherstyle{this is the \textit{nested part} some more text...}}
Теперь я хочу удалить все \SomeStyle{...}
, но не содержимое. Содержимое содержит вложенные скобки. Строка выше должна стать:
\otherstyle{this is the \textit{nested part} some more text...}
Вопросы:
- Предлагает ли какой-либо редактор LaTeX способ сделать это?
- Какой редактор/скрипт делает это?
- Как это сделать с помощью sed? [🤓]
Мое решение — это bash-скрипт, использующий sed.
- подготовить текст: отметить строку замены с помощью ASCII звонка, добавить новую строку после каждой скобки
- цикл: найти { -> добавить X в холд-спейс, найти } -> удалить X из холд-спейса, холд-спейс пуст -> удалить закрывающую }
- восстановить новые строки и ASCII звонок в предыдущий вид
Скрипт работает, но не справляется с:
\badstyle{w}\badstyle{o}\badstyle{r}\badstyle{d}
Он станет:
wo}rd}
Ветвление к :f, кажется, не работает.
F=$(sed 's|\\|\\\\|g;s|{|\\{|g' <<< "$1" )
# отметить все строки для удаления с помощью ASCII звонка и новой строки
# добавить новую строку после каждой { и }
SEDpre="
s|""$F"'|\a%\n|g
s|\{|\{\n|g
s|\}|\}\n|g
'
SEDpost="
:a;N;$!ba;
s|\a%\n||g
s|\{\n|\{|g
s|\}\n|\}|g
"
# подсчитать скобки
SED='
/\a%/{
:a
n
:f
/\{/{x;s|$|X|;x;ba}
/\}/{x;
s|X||;
/^$/{x;bb}
x
ba
}
}
b
:b
/\}/{
s|\}||;
N;
s|\n||;
/\a%/bf
}
'
sed -r -E "$SEDpre" "$2" | sed -rE "$SED" | sed -rE "$SEDpost"
Типичный подход — использовать рекурсивную возможность регулярных выражений perl
:
perl -0777 -pe 's/\\SomeStyle(\{((?:(?1)|[^{}])*)\})/$2/gs' file.tex
(добавьте опцию -i
для редактирования файла на месте i
).
Выше (?1)
подобно вставке регулярного выражения в первую пару (...)
, так что (\{((?:(?1)|[^{}])*)\})
на этой позиции.
Вот возможный механизм для sed
. Для простоты предполагаем, что нет символов подчеркивания, поэтому мы можем использовать один в качестве маркера. Это аналогично вашему ASCII звонку. Мы вставляем маркер в начало строки и перемещаем его символ за символом до конца строки. Каждый раз, когда он перемещается через {
, мы добавляем знак +
в начало строки в качестве счетчика. Каждый раз, когда он перемещается через }
, мы удаляем знак +
из начала. Если у нас больше нет знаков +
, значит, мы сбалансировали скобки и можем применить замену, необходимую до маркера.
В случае, если строка уже начинается с +
, мы начинаем с добавления !!
в начало и удаляем это в конце.
sed '
s/^/!!_/
:a
/_\(.\)/{
s//\1_/
/{_/{
s/^/+/
}
/}_/{
/^+/!{
s/^/mismatch{}/
b
}
s///
/^!!/b b
}
b a
}
# движения сюда, если _ в конце строки
:b
# фиктивный переход на очистку, чтобы можно было обнаружить, если s выполнено
t c
:c
s/\\SomeStyle{\(.*\)}_/\1_/
s/\\badstyle{\(.*\)}_/\1_/
# повторяем, чтобы выполнить глобально на строке
t a
s/^!!//
s/_$//
'
Ответ или решение
Вопрос о том, как удалить определенные конструкции в LaTeX, сохраняя при этом содержимое, представляет собой интересную задачу, особенно когда дело касается вложенных фигурных скобок. В данном случае речь идет о необходимости убрать оболочку команды \SomeStyle{...}
, не затрагивая находящееся внутри содержимое, которое может включать другие команды с вложенными скобками.
Теоретическая часть
В TeX и LaTeX фигурные скобки {}
используются для определения границ аргументов команд. Например, конструкция \SomeStyle{...}
означает применение стиля \SomeStyle
ко всему, что заключено в фигурные скобки. Задача заключается в удалении этой оболочки, но без изменения вложенного содержимого.
Для решения этой задачи первоочередной проблемой является обработка вложенных фигурных скобок. Инструменты обработки текстов, такие как sed
и perl
, предоставляют мощные средства для поиска и замены строк на основе регулярных выражений, но их подходы существенно различаются. В частности, perl
поддерживает рекурсивные регулярные выражения, которые позволяют более удобно работать со вложенными структурами, в отличие от sed
, где требуется более сложное манипулирование текстом для достижения аналогичного эффекта.
Пример решения и его применение
Перейдём к рассмотрению практических решений данной задачи с использованием как perl
, так и sed
.
Использование Perl
Perl
однозначно выделяется среди текстовых редакторов благодаря поддержке рекурсивных регулярных выражений. Вот пример команды, которая достигает цели:
perl -0777 -pe 's/\\SomeStyle(\{((?:(?1)|[^{}])*)\})/$2/gs' file.tex
На этом примере можно увидеть мощь рекурсивных регулярных выражений:
-
(?1)
— это рекурсивная ссылка на первое подвыражение в круглых скобках,(\{((?:(?1)|[^{}])*)\})
, что позволяет обработать любое количество вложенных фигурных скобок. -
Опция
-0777
объединяет всю обработку файла в одно единственное строковое выражение (захватывает всю строку).
Этот подход позволяет "развернуть" любую конструкцию верхнего уровня типа \SomeStyle{...}
с множеством вложенных скобок.
Пример с использованием Sed
Поскольку sed
не поддерживает рекурсивные регулярные выражения, решение оказывается более сложным и требует пошагового манипулирования как исходным текстом, так и дополнительными маркерами:
sed '
s/^/!!_/
:a
/_\(.\)/{
s//\1_/
/{_/{
s/^/+/
}
/}_/{
/^+/!{
s/^/ошибка{}/
b
}
s///
/^!!/b b
}
b a
}
:b
# Переход после обработки каждой строки
# Удаляем команду \SomeStyle, заменяя ее содержимым
s/\\SomeStyle{\(.*\)}_/\1_/
# Повторяем для каждой вложенной строки
t a
s/^!!//
s/_$//
' file.tex
Здесь используются специальные символы (например, _
) для перемещения по строке и +
как счетчик, обрабатывающий каждое появление {
и }
. Как только счетчик становится пустым, можно выполнить необходимую подстановку.
Заключение
Эти решения показывают, как можно эффективно использовать скрипты и текстовые редакторы для обработки сложных строковых конструкций. В зависимости от сложности задачи и доступных инструментов выбор между perl
и sed
может варьироваться. Perl
предпочтителен в случаях, когда требуется удобство и мощь рекурсивных выражений, тогда как sed
может использоваться, когда доступ к perl
затруднён или необходимость обработки текста возникает в контексте Unix-скриптов, где sed
является стандартом.
Таким образом, каждой из подходов имеет свои плюсы и минусы, и выбор инструмента должен основываться на конкретных технических требованиях проекта и опыте разработчика.