Поиск и замена только в определенном разделе - PullRequest
2 голосов
/ 09 апреля 2019

У меня есть пара тысяч файлов .html, и мне нужно найти и заменить жестко закодированное имя сервера на относительный путь, но ТОЛЬКО в нижнем колонтитуле.

например.

<body>
   <a href="http://hardcoded/something">This is ok</a>      
   ... much more content here
   <div class="footer">
       <a href="http://hardcoded/something">Change this one</a>      
   </div>
</body>

Существует ли какой-либо инструмент для поиска и замены?

Ответы [ 3 ]

1 голос
/ 10 апреля 2019
:- use_module(library(dcg/basics)).

:- set_prolog_flag(double_quotes, codes). 

dcg_change_004(Html) -->
    string(Footer_prefix_codes),
    { Footer_start_tag_codes = "<div class=\"footer\">" },
    Footer_start_tag_codes,
    string(Anchor_prefix_codes),
    anchor(Anchor_codes),
    remainder(Rest_codes), !,
    {
        flatten([Footer_prefix_codes,Footer_start_tag_codes,Anchor_prefix_codes,Anchor_codes,Rest_codes],Codes),
        string_codes(Html,Codes)
    }.

anchor("<a href=\"http://changed/something\">") -->  
  "<a href=\"http://hardcoded/something\">".

Да, код действительно такой маленький !!!


Это работает, считая HMTL потоком символов, а не структурой, такой как DOM или XHTML , что значительно упрощает проблему. Этот метод не может быть использован во всех случаях, но он достаточен для решения проблемы, поставленной в этом вопросе.

Подробнее об ограничениях этой техники см. this .

Два из предложений, используемых в этой версии , anything//1 и rest_2//1, можно заменить на предложения из библиотеки.

basics.pl - Различные общие утилиты DCG

Библиотека добавляется к этому с

:- use_module(library(dcg/basics)).

Как работает код:

Читать все до <div class="footer">

string(Footer_prefix_codes),
{ Footer_start_tag_codes = "<div class=\"footer\">" },
Footer_start_tag_codes

Примечание: "<div class=\"footer\">" привязано к переменной, потому что это необходимо дважды, один раз для сопоставления ввода и один раз как часть вывода. Помещая его в переменную, его не нужно вводить дважды.

, то читать все до <a href="http://hardcoded/something">

string(Anchor_prefix_codes)

и замените его на <a href="http://changed/something">

anchor(Anchor_codes)

, который работает с

anchor("<a href=\"http://changed/something\">") -->  
  "<a href=\"http://hardcoded/something\">".

, то прочитайте все, что осталось.

remainder(Rest_codes)

По пути DCG собирал коды символов в список,

Footer_prefix_codes
Footer_start_tag_codes
Anchor_prefix_codes
Anchor_codes
Rest_codes

Они сведены в один список с помощью

flatten([Footer_prefix_codes,Footer_start_tag_codes,Anchor_prefix_codes,Anchor_codes,Rest_codes],Codes)

и список кодов символов Codes преобразуется в строку с

string_codes(Html,Codes)

с Html результатом.

Вот тестовый пример

:- begin_tests(html_dcg).

test(004) :-
    HTML_in = "\c
<body>
   <a href=\"http://hardcoded/something\">This is ok</a>
   <div class=\"footer\">
       <a href=\"http://hardcoded/something\">Change this one</a>
   </div>
</body>",
    Expected_HTML_out = "\c
<body>
   <a href=\"http://hardcoded/something\">This is ok</a>
   <div class=\"footer\">
       <a href=\"http://changed/something\">Change this one</a>
   </div>
</body>",
    string_codes(HTML_in,HTML_in_codes),
    DCG = dcg_change_004(HTML_out),
    phrase(DCG,HTML_in_codes,Rest),
    format('~nHTML: ~n`~w''~n',[HTML_out]),
    assertion( HTML_out == Expected_HTML_out ),
    assertion( Rest == [] ).

:- end_tests(html_dcg).

Пример запуска тестового примера:

?- run_tests(html_dcg:4).
% PL-Unit: html_dcg:4 
HTML: 
`<body>
   <a href="http://hardcoded/something">This is ok</a>
   <div class="footer">
       <a href="http://changed/something">Change this one</a>
   </div>
</body>'
. done
% test passed
true.

Это действительно так просто с DCG. В некоторых отношениях DCG похожи на BNF и похожи на регулярные выражения; в иерархии Хомского они более могущественны, чем регулярные выражения. Поэтому, если Regular Expression сводит вас с ума, и вы не хотите писать много кода с парсером или анализировать конфликтные правила с помощью парсера, переключитесь на DCG.

Наслаждайтесь.


Пролог-код для поиска в каталогах файлов типа html.

test_01 :-
    Directory = 'C:\\Something',
    process_directory(Directory,[],Items),
    print_paths(Items).

process_directory(Directory,Items0,Items) :-
    directory_files(Directory,Files),
    process_files(Directory,Files,Items0,Items).

process_files(Directory,[File|Files],Items0,Items) :-
    process_file(Directory,File,Items0,Items1),
    process_files(Directory,Files,Items1,Items), !.
process_files(_Directory,[],Items,Items).

process_file(Directory,File,Items0,Items) :-
    (
        File = '.',
        Items = Items0
    ;
        File = '..',
        Items = Items0
    ;
        directory_file_path(Directory, File, Path),
        exists_directory(Path),
        process_directory(Path,Items0,Items1),
        Items = Items1
    ;
        directory_file_path(Directory, File, Path),
        exists_file(Path),
        (
            file_name_extension(_Name, 'html', File),
            Items = [Path|Items0]
        ;
            Items = Items0
        )
    ;
        Items = Items0
    ).

print_paths([Path|Paths]) :-
    format('~w~n',Path),
    print_paths(Paths).
print_paths([]).

Этот раздел кода я не проверял из-за утомительного создания тестовых данных, поэтому проверьте его перед использованием.

Сделайте резервную копию своего каталога перед использованием, если вы точно не знаете, что делаете. Одна ошибка, и она уничтожит все ваши файлы, потому что она записывает много файлов во многих каталогах.

change_footer(Directory) :-
    process_directory(Directory,[],Paths),
    print_paths(Paths),
    change_files(Paths).

change_files([Path|Paths]) :-
    open(Path,write,Stream),
    read_stream_to_codes(Stream,Codes),
    DCG = dcg_change_004(HTML),
    phrase(DCG,Codes),
    format(Stream,HTML,[]),
    close(Stream),
    change_files(Paths).
change_files([]).
1 голос
/ 09 апреля 2019

РЕДАКТИРОВАТЬ

После публикации этого ответа я уточнил его до лучшего ответа .Этот ответ оставлен, потому что он содержит большинство комментариев и служит отправной точкой для уточнения кода.


Полный код:

change.pl

:- set_prolog_flag(double_quotes, codes).

eos([], []).

dcg_change_002(Html) -->
    { Footer_start_tag = "<div class=\"footer\">" },
    anything(Footer_prefix),
    Footer_start_tag, !,
    anything(Anchor_prefix),
    anchor_2(Anchor), !,
    rest_2(Rest), !,
    {
        string_codes(Anchor_prefix,Anchor_prefix_codes),
        string_codes(Anchor,Anchor_codes),
        string_codes(Rest,Rest_codes),
        append(Footer_prefix,Footer_start_tag,Part_1),
        append(Part_1,Anchor_prefix_codes,Part_2),
        append(Part_2,Anchor_codes,Part_3),
        append(Part_3,Rest_codes,Html)
    }.

anything([]) --> [].
anything([C|Cs]) -->
     [C],
     anything(Cs).

rest_2([]) --> call(eos).
rest_2([C|Cs]) -->
    \+ call(eos),
     [C],
     rest_2(Cs).

anchor_2("<a href=\"http://changed/something\">") --> "<a href=\"http://hardcoded/something\">".

Контрольный пример:

:- begin_tests(html_dcg).

test(002) :-
    HTML_in = "\c
<body>
   <a href=\"http://hardcoded/something\">This is ok</a>
   <div class=\"footer\">
       <a href=\"http://hardcoded/something\">Change this one</a>
   </div>
</body>",
    Expected_HTML_out = "\c
<body>
   <a href=\"http://hardcoded/something\">This is ok</a>
   <div class=\"footer\">
       <a href=\"http://changed/something\">Change this one</a>
   </div>
</body>",
    string_codes(HTML_in,HTML_in_codes),
    DCG = dcg_change_002(HTML_out_codes),
    phrase(DCG,HTML_in_codes,Rest),
    string_codes(HTML_out,HTML_out_codes),
    format('~nHTML: ~n`~w''~n',[HTML_out]),
    assertion( HTML_out == Expected_HTML_out ),
    assertion( Rest == [] ).

:- end_tests(html_dcg).

Пример выполнения теста:

SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
Please run ?- license. for legal details.

For online help and background, visit http://www.swi-prolog.org
For built-in help, use ?- help(Topic). or ?- apropos(Word).

?- consult("C:/change.pl").
true.

?- run_tests.
% PL-Unit: html_dcg 
HTML: 
`<body>
   <a href="http://hardcoded/something">This is ok</a>
   <div class="footer">
       <a href="http://changed/something">Change this one</a>
   </div>
</body>'
. done
% test passed
true.

Обычно тест не выдает результат, например, format('~nHTML: ~n``~w''~n',[HTML_out]), ноон был добавлен, чтобы вы могли видеть результат без стандартного кода в тесте.

Поскольку этот код ближе к тому, что должно быть сделано, здесь есть объяснение.

Пролог обычно пишется с использованием предикатов, они используют оператор :-.DCG разные и используют -->.DCG преобразуется в обычный Prolog, и DCG могут включать обычный Prolog с использованием { ... }.

DCG-кодов символов процесса, в данном случае, поскольку это весь текст ASCII, вы можете использовать таблицу ASCII , но пытаться прочитать список символов ASCII сложно, поэтому

:- set_prolog_flag(double_quotes, codes).

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

тест-драйвы это так, начиная с тестового примера

:- begin_tests(html_dcg).
:- end_tests(html_dcg).

установить тестовый модуль с именем htm_dcg и

test(002) :-

иметь тестовый предикат с именем 002.

HTML_in = "\c
<body>
   <a href=\"http://hardcoded/something\">This is ok</a>
   <div class=\"footer\">
       <a href=\"http://hardcoded/something\">Change this one</a>
   </div>
</body>"

При этом используется = / 2 (объединение), которое не является присваиванием, для привязки текста HTML к переменной HTML_in, но поскольку это отдельный модуль, а не код, это строкаи не конвертируется в список кодов символов.\c - это escape-символ , который позволяет начинать <body> со следующей строки без добавления \n к входу.Кроме того, " необходимо экранировать для Пролога как \".

Expected_HTML_out = "\c
<body>
   <a href=\"http://hardcoded/something\">This is ok</a>
   <div class=\"footer\">
       <a href=\"http://changed/something\">Change this one</a>
   </div>
</body>"

То же самое для Expected_HTML_out.

Поскольку DCG требует коды, преобразуйте строку в коды с помощью

string_codes(HTML_in,HTML_in_codes)

На самом деле следующие две строки будут записаны как одна

phrase(dcg_change_002(HTML_out_codes),HTML_in_codes,Rest)

, но это будет немного длинно и запутанно.

фразу / 3 что является переходом от предикатов к DCG и почему я в этом примере явно записываю следующие две строки как

DCG = dcg_change_002(HTML_out_codes),
phrase(DCG,HTML_in_codes,Rest)

, чтобы вы могли видеть, что dcg_change_002/2 является DCG и возвращает HTML-результат.Он назван codes, чтобы показать, что он возвращается как список кодов символов, а не как строка.Rest более чем уничтожен, но используется для обнаружения некоторых редких ошибок, работающих с

 assertion( Rest == [] )

Поскольку HTML возвращается в виде списка кодов символов, он преобразуется обратно в строку с

string_codes(HTML_out,HTML_out_codes)

, чтобы его можно было использовать с

format('~nHTML: ~n`~w''~n',[HTML_out])

, чтобы распечатать HTML-код для демонстрации правильной работы, и

assertion( HTML_out == Expected_HTML_out )

, чтобы показать, что код возвращает ожидаемый результат.

Что касается DCG, точка входа -

dcg_change_002(Html) -->

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

{ Footer_start_tag = "<div class=\"footer\">" }

Так что проблемаэто захватить весь текст до Footer_start_tag, и это делается с помощью

anything(Footer_prefix)

, а затем совпадение с Footer_start_tag

Footer_start_tag, !,

! - остановкаотступает и слишком продвинут для этого обсуждения, но повышает производительность, но его использование не одобряется в кругах чистоты (длинное обсуждение, не спрашивайте).

anything(Anchor_prefix)

Теперь, когда мы находимся в нижнем колонтитулевесь текст до Anchor.

    anchor_2(Anchor), !,

Здесь Anchor находится в

anchor_2("<a href=\"http://changed/something\">") -->  
    "<a href=\"http://hardcoded/something\">".

, что соответствует коду, который вы хотите заменить

"<a href=\"http://hardcoded/something\">".

и вернуть код, который вы хотите изменитьэто

 "<a href=\"http://changed/something\">"

Вы можете фактически составить таблицу из этих правил и изменить сразу несколько совпадений, таких как этот якорь, если вы хотите использовать одно и то же место на входе,

и, наконец,возьмите оставшуюся часть текста.

rest_2(Rest), !,

Теперь для части кода я все еще не доволен.

Так как все это в пределах { ... }, это не DCG, а обычныйПролог встроен в DCG.

{

    string_codes(Anchor_prefix,Anchor_prefix_codes),
    string_codes(Anchor,Anchor_codes),
    string_codes(Rest,Rest_codes),

, которые являются более преобразованиями строки в коды.

    append(Footer_prefix,Footer_start_tag,Part_1),
    append(Part_1,Anchor_prefix_codes,Part_2),
    append(Part_2,Anchor_codes,Part_3),
    append(Part_3,Rest_codes,Html)

, который складывает весь список символов обратно в один список и связывает переменную HTML с результатом.

}.

} просто выходит из встроенного кода.

anything([]) --> [].
anything([C|Cs]) -->
     [C],
     anything(Cs).

Это стандартный рекурсивный вызов, который просто захватывает отдельные символы C и объединяет их в список, используя |.

rest_2([]) --> call(eos).
rest_2([C|Cs]) -->
    \+ call(eos),
     [C],
     rest_2(Cs).

Еще один стандартный рекурсивный вызов, который просто захватывает отдельные символы C и встраивает их в список, используя |, но этот ищет End Of Stream, то есть eos. \+ - это способ Пролога этого не делать.

Я знаю для многих, если не для большинства, кто читает это, это выглядит просто, но на самом деле все должно быть так просто. Причина, по которой вы не видите и не слышите, как это делают другие программисты, заключается в том, что вы изучаете программирование логики, например, Пролог сложен и даже тогда многие занятия по Прологу никогда не доходят до DCG. Мне потребовались годы, чтобы подняться на этот уровень, и даже тогда, по большинству стандартов, этот код не так хорош, он выполнит свою работу, быстр в написании и достаточно универсален.

Я надеюсь опубликовать еще более продвинутую и более простую версию, но, честно говоря, это первый раз, когда я пытался использовать DCG с HTML.

0 голосов
/ 12 апреля 2019
package custom;

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;

public class Main {

    public static void main(String[] args) throws IOException {
        String startPath = "/Users/me/someProject/public";

        Files.walkFileTree(Paths.get(startPath), new SimpleFileVisitor<>(){
            @Override
            public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {

                if (!path.toString().endsWith(".html")) {
                    return FileVisitResult.CONTINUE;
                }
                var content = new String(Files.readAllBytes(path), "UTF-8");

                var fixedContent = replaceAfter(content,
                        "<div class=\"footer\">",
                        "http://hardcoded/something",
                        "/something");

                Files.write(path, fixedContent.getBytes());
                return FileVisitResult.CONTINUE;
            }
        });
    }

    /**
     * Replaces the `searchTerm` with `replacement` but only after the ocurrence of `afterMark`
     * If `afterMark` is not present nothing is changed
     * @param source - Original string
     * @param afterMark - Will perform the replacement after this mark is found
     * @param searchTerm - What to replace
     * @param replacement - replacement
     * @return a new string with the search term replaced
     */
    public static String replaceAfter(String source, String afterMark, String searchTerm, String replacement) {
        var i = source.indexOf(afterMark);
        if ( i < 0 ) {
            return source;
        }
        var replaced = source.substring(i).replace(searchTerm, replacement);
        return source.substring(0, i) + replaced;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...