仅在特定部分上搜索和替换

时间:2019-04-09 15:23:17

标签: html language-agnostic

我有成千上万个.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 个答案:

答案 0 :(得分:1)

编辑

发布此答案后,我将其精简为better answer。 之所以留下这个答案,是因为它包含了大多数注释,并且是改进代码的垫脚石。


完整代码:

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]),但它已添加,因此您可以在测试中看到没有样板代码的结果。

由于这一步更接近此处应做的解释。

Prolog通常使用谓词编写,它们使用运算符:-。 DCG不同,使用-->。 DCG已转换为常规Prolog,DCG可以使用{ ... }包含常规Prolog。

DCG会处理字符代码,在这种情况下,因为这都是ASCII文本,因此可以使用ASCII table,但是尝试读取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,但是由于这是与代码不同的模块,因此它是字符串,不会转换为字符代码列表。 \cescape character,它允许<body>从下一行开始,而无需在输入中添加\n。同样,"也必须以\"的形式转义进入Prolog。

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)

但这有点长且令人困惑。

phrase/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中的常规Prolog。

{

    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\+是Prolog不这样做的方式。

我知道,即使不是大多数阅读本书的人,这看起来也很简单,但实际上应该是如此简单。您看不到或听到更多程序员这样做的原因是学习逻辑编程,例如Prolog很难,即使那样,Prolog上的许多类也从未接近DCG。我花了好几年的时间才能达到这个级别,即使按大多数标准,此代码也不是那么好,它可以完成工作,并且编写速度很快,用途广泛。

我希望发布另一个更高级,更简单的版本,但实际上这是我第一次尝试将DCG与HTML结合使用。

答案 1 :(得分:1)

:- 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这样的结构来工作,这大大简化了问题。并非在所有情况下都可以使用此技术,但足以解决此问题中提出的问题。

有关此技术局限性的更多详细信息,请参见this

versionanything//1rest_2//1中使用的两个子句可以用库中的子句替换。

basics.pl -- Various general DCG utilities

将库添加到其中

:- 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和正则表达式;在Chomsky Hierarchy中,它们比正则表达式更强大。因此,如果正则表达式使您发疯,并且您不想使用解析器编写大量样板代码或不想使用解析器来处理解析冲突规则,请切换到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([]).

答案 2 :(得分:0)

const express = require('express')
const bodyParser = require('body-parser')
const app = express()

app.set('view engine', 'ejs')

app.use(express.static('public'));
app.use(bodyParser.urlencoded({ extended: true }));

app.get('/', function (req, res) {
  res.render('index');
})

app.post('/', function (req, res) {
  res.render('index');
  console.log("server response");
})

app.listen(3000, function () {
  console.log('Example app listening on port 3000!')
})