首页 > 解决方案 > 仅在特定部分搜索和替换

问题描述

我有几千个 .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>

是否有任何工具可以进行这种搜索和替换?

标签: htmllanguage-agnostic

解决方案


:- 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 视为字符流而不是诸如DOMXHTML之类的结构来实现的,这极大地简化了问题。这种技术不能在所有情况下都使用,但足以解决这个问题中提出的问题。

有关此技术限制的更多详细信息,请参阅

版本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 和正则表达式;在乔姆斯基层次结构中,它们比正则表达式更强大。因此,如果正则表达式让您发疯,并且您不想使用解析器编写大量样板代码或使用解析器对抗解析冲突规则,请切换到 DCG。

享受。


Prolog 代码用于在目录中搜索类型为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([]).

推荐阅读