Замените все символы «\», которые * не * внутри тегов <code> "

Сначала первое: ни это , ни это , ни это не отвечали на мой вопрос. Поэтому я открою новый.

Пожалуйста прочти

Ладно ладно. Я знаю, что регулярные выражения не являются способом анализа общего HTML. Обратите внимание, что созданные документы записываются с использованием ограниченного контролируемого подмножества HTML. И люди, пишущие документы, знают, что они делают. Это все ИТ-специалисты!

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

Я не пытаюсь загружать произвольные документы из Интернета и разбирать их!

И если разбор не выполняется , документ редактируется, поэтому он будет разбираться. Проблема, которую я здесь рассматриваю, более общая, чем эта (т. Е. Не заменять шаблоны внутри двух других шаблонов).

Немного фона (вы можете пропустить это …)

В нашем офисе мы должны «печатать» нашу документацию. Поэтому почему некоторые придумали все это в документы Word. До сих пор мы, к счастью, еще не совсем там. И, если я это сделаю, нам может не понадобиться.

Текущее состояние (… и это)

Основная часть документов хранится в базе данных TikiWiki. Я создал плотный PHP-скрипт, который конвертирует документы из HTML (через LaTeX) в PDF. Одним из необходимых свойств выбранной Wiki-системы был редактор WYSIWYG. Что, как и ожидалось, оставляет нам документы с менее формальным DOM.

Следовательно, я транслитерирую документ, используя «простые» регулярные выражения. Пока все работает (в основном), но я столкнулся с одной проблемой, которую еще не выяснил сам.

Проблема

Некоторые специальные символы необходимо заменить на разметку LaTeX. Для exaple символ \ должен быть заменен на $\backslash$ (если кто-то не знает другое решение?).

Кроме того, в то время как в verbatim !

Я заменяю теги <code> на verbatim разделы. Но если этот блок code содержит обратную косую черту (как в случае с именами папок Windows), сценарий все еще заменяет эти обратные косые черты.

Я считаю, что могу решить это, используя отрицательные LookBehinds и / или LookAheads. Но мои попытки не сработали.

Конечно, мне было бы лучше с реальным парсером. Фактически, это что-то в моей «мозговой карте», но в настоящее время она выходит за рамки. Скрипт работает достаточно хорошо для нашей ограниченной области знаний. Создание парсера потребует от меня начать с нуля.

Моя попытка

Пример ввода

 The Hello \ World document is located in: <code>C:\documents\hello_world.txt</code> 

Ожидаемый результат

 The Hello $\backslash$ World document is located in: \begin{verbatim}C:\documents\hello_world.txt\end{verbatim} 

Это лучшее, что я мог придумать до сих пор:

 <?php $patterns = array( "special_chars2" => array( '/(?<!<code[^>]*>.*)\\\\[^$](?!.*<\/code>)/U', '$\\backslash$'), ); foreach( $patterns as $name => $p ){ $tex_input = preg_replace( $p[0], $p[1], $tex_input ); } ?> 

Обратите внимание, что это только отрывок, а [^$] – другое требование LaTeX.

Еще одна попытка, которая, казалось, работала:

 <?php $patterns = array( "special_chars2" => array( '/\\\\[^$](?!.*<\/code>)/U', '$\\backslash$'), ); foreach( $patterns as $name => $p ){ $tex_input = preg_replace( $p[0], $p[1], $tex_input ); } ?> 

… другими словами: уклонение от негативного взгляда.

Но это выглядит более подверженным ошибкам, чем с lookbehind и lookahead.

Связанный с этим вопрос

Как вы, возможно, заметили, шаблон неровный ( /.../U ). Так будет ли это соответствовать как можно меньше внутри блока <code> ? С учетом взглядов?

Если я, я попытаюсь найти HTML-парсер и сделаю с этим.

Другой вариант – попытаться вырезать строку в <code>.*?</code> и другие части .

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

 $x="The Hello \ World document is located in:\n<br> <code>C:\documents\hello_world.txt</code>"; $r=preg_split("/(<code>.*?<\/code>)/", $x,-1,PREG_SPLIT_DELIM_CAPTURE); for($i=0;$i<count($r);$i+=2) $r[$i]=str_replace("\\","$\\backslash$",$r[$i]); $x=implode($r); echo $x; 

Вот результаты.

 The Hello $\backslash$ World document is located in: C:\documents\hello_world.txt 

Извините, если мой подход не подходит для вас.

Я считаю, что могу решить это, используя отрицательные LookBehinds и / или LookAheads.

Вы ошибаетесь. Регулярные выражения не заменяют синтаксический анализатор .

Я бы предложил вам передать html через htmltidy, затем прочитать его с помощью dom-parser, а затем преобразовать dom в ваш целевой формат вывода. Есть ли что-то, препятствующее вам пройти этот маршрут?

Parser FTW, хорошо. Но если вы не можете использовать парсер, и можете быть уверены, что теги <code> никогда не вложены, вы можете попробовать следующее:

  1. Найдите разделы <code>.*?</code> вашего файла (возможно, вам нужно включить режим «Точечные совпадения-новые строки»).
  2. Замените все обратные косые черты внутри этой секции чем-то уникальным, например #?#?#?#
  3. Замените раздел, найденный в 1, этим новым разделом
  4. Замените все обратные косые черты на $\backslash$
  5. Замените als <code> на \begin{verbatim} и все </code> на \end{verbatim}
  6. Замените #?#?#?# \

FYI, регулярные выражения в PHP не поддерживают переменную длину lookbehind. Таким образом, это затруднение условного совпадения между двумя границами.

Pandoc? Pandoc конвертирует между кучей форматов. вы также можете объединить кучу мух вместе, а затем скрывать их. Может быть, несколько сценариев оболочки в сочетании с вашими скриптами scpping php?

С вашим «ожидаемым вводом» и командой pandoc -o text.tex test.html вывод:

 The Hello \textbackslash{} World document is located in: \verb!C:\documents\hello_world.txt! 

pandoc может читать из stdin, писать в stdout или напрямую в файл.

Если ваши блоки <code> не вложены, это регулярное выражение обнаружит обратную косую черту после ^ начала строки или </code> без <code> между ними.

 ((?:^|</code>)(?:(?!<code>).)+?)\\ | | | | | \-- backslash | \-- least amount of anything not followed by <code> \-- start-of-string or </code> 

И замените его на:

 $1$\backslash$ 

Вы должны запустить это регулярное выражение в режиме «singleline», так что . соответствует новым строкам. Вам также придется запускать его несколько раз, указав глобальную замену недостаточно. Каждая замена заменяет только первую допустимую обратную косую черту после начала строки или </code> .

Напишите парсер на основе анализатора HTML или XML, такого как DOMDocument . Пройдите анализируемую DOM и замените \ на каждый текстовый узел, который не является потомком узла code с $\backslash$ и каждым узлом, который является узлом code с \begin{verbatim} … \end{verbatim} .