【问题标题】:Trouble Getting Regular Expression To Work正则表达式无法正常工作
【发布时间】:2011-01-30 02:26:56
【问题描述】:

我正在尝试使用正则表达式从文本文件中删除某些编码块。到目前为止,我的大部分正则表达式行都可以删除代码。但是,我有两个问题:

1) 每当我删除一段文本时,应该将文本替换为空格,而不是简单地被删除。 我的正则表达式代码的一个示例是:

$file =~ s/<ul(.*)>//gi;

这会删除所有基本格式为&lt;ul...&gt; 的行,这正是我想要的。但是,如前所述,它将标签和所有包含的数据替换为空格,我想知道如何停止这种特殊的替换。

2) 某些本应有效的正则表达式代码似乎无效。例如,我想删除

<script type="text/javascript"> 

function getCookies() { return ""; }

</script>

我尝试过使用各种正则表达式代码,但似乎没有什么可以删除这些行。例如:

$file =~ s/<script type(.*)<\/script>//gi;

分别删除&lt;script type...&gt;&lt;/script&gt;标签,但留下

function getCookies() { return ""; }

...完好无损。我不确定为什么会发生这种情况,我非常想纠正这个问题。这怎么可能?对这两个问题中的任何一个的任何帮助都会非常有帮助!

编辑:对不起,我正在使用 Perl! 另外:我刚刚尝试使用

$file =~ /<script type(.*)<\/script>/sgi

...以及 /msgi,但不幸的是,两者都不起作用。 &lt;script type&gt;&lt;/script&gt; 标签都被删除了,但由于某种原因

function getCookies() { return ""; } 

...部分保留。这是我的整个代码,包括所有正则表达式:

use strict;
use warnings;

my $firstarg;
if ($ARGV[0]){
  $firstarg = $ARGV[0];
}

open (DATA, $ARGV[1]);
my $file = do {local $/; <DATA>};

$file =~ s/<\!DOCTYPE(.*)>//gi;
$file =~ s/<html>//gi;
$file =~ s/<\/html>//gi;
$file =~ s/<title>//gi;
$file =~ s/<\/title>//gi;
$file =~ s/<head>//gi;
$file =~ s/<\/head>//gi;
$file =~ s/<link(.*)>//gi;
$file =~ s/<\link>//gi;
$file =~ s/CDM(.*)\;//gi;
$file =~ s/<\!(.*)->//gi;
$file =~ s/<body(.*)>//gi;
$file =~ s/<\/body>//gi;
$file =~ s/<div(.*)>//gi;
$file =~ s/<\/div>//gi;
$file =~ s/function(.*)>//gi;
$file =~ s/<noscript>//gi;
$file =~ s/<\/noscript>//gi;
$file =~ s/<a(.*)>//gi;
$file =~ s/<\/a>//gi;
$file =~ s/<ul(.*)>//gi;
$file =~ s/<\/ul>//gi;
$file =~ s/<li(.*)>//gi;
$file =~ s/<\/li>//gi;
$file =~ s/<form(.*)>//gi;
$file =~ s/<\/form>//gi;
$file =~ s/<iframe(.*)>//gi;
$file =~ s/<\/iframe>//gi;
$file =~ s/<select(.*)>//gi;
$file =~ s/<\/select>//gi;
$file =~ s/<textarea(.*)>//gi;
$file =~ s/<\/textarea>//gi;
$file =~ s/<b>//gi;
$file =~ s/<\/b>//gi;
$file =~ s/<H1>//gi;
$file =~ s/<H2>//gi;
$file =~ s/<H3>//gi;
$file =~ s/<H4>//gi;
$file =~ s/<H5>//gi;
$file =~ s/<H6>//gi;
$file =~ s/<\/H1>//gi;
$file =~ s/<\/H2>//gi;
$file =~ s/<\/H3>//gi;
$file =~ s/<\/H4>//gi;
$file =~ s/<\/H5>//gi;
$file =~ s/<\/H6>//gi;
$file =~ s/<option(.*)>//gi;
$file =~ s/<\/option>//gi;
$file =~ s/<p>//gi;
$file =~ s/<\/p>//gi;
$file =~ s/<span(.*)>//gi;
$file =~ s/<\/span>//gi;
$file =~ s/<!doctype(.*)>//gi;
$file =~ s/<base(.*)>//gi;
$file =~ s/<br>//gi;
$file =~ s/<hr>//gi;
$file =~ s/<img(.*)>//gi;
$file =~ s/<input(.*)>//gi;
$file =~ s/<link(.*)>//gi;
$file =~ s/<meta(.*)>//gi;
$file =~ s/<script type(.*)<\/script>//gi;
print $file;

好的,现在我删除了导致一个问题的 &lt;script&gt; 正则表达式,另一个已创建 - 使用:

$file =~ s/<script type(.*)<\/script>//gi;

删除&lt;script ...&gt; 的第一个实例之间的所有内容,但不删除标签本身,而不是整个标签的重复。使用:

$file =~ s/<script type(.*)<\/script>//mgi;

结果完全相同。使用:

$file =~ s/<script type(.*)<\/script>//sgi;

导致打印几个换行符,但没有其他文本,/msgi 相同。 呃,问题永远不会结束...... :(

新编辑:对于发布有关使用正则表达式解析 HTML 的问题,我深表歉意。我意识到编程社区内对这种做法存在相当大的反对意见(或尝试实践,因为这似乎经常失败)。但是,不幸的是,我不得不使用正则表达式来解析 selected HTML,这些 HTML 可以删除大部分(如果不是全部)HTML 标记。我不允许使用模块,尽管这是最明显和最简单的答案。

【问题讨论】:

  • 因为=~ 我假设你在这里写Perl?关于环境的更多信息会很有用:)
  • 对于#2,将 /gi 更改为 /msgi 以处理多行模式。
  • 请查看我在答案链接中发布的解决方案,尤其是第二个。这就是使用解析器的原因。
  • 使用HTML::TreeBuilder模块。如果您在阅读this 后还没有足够的动力这样做,我不知道什么能说服您。
  • 我不允许在此作业中使用模块。它应该是纯正则表达式代码,减去输入/搜索代码。相信我,我很想使用一个模块,它会让我的生活变得更轻松。 :(

标签: html regex perl


【解决方案1】:

如果你不能使用除了 Perl 正则表达式之外的任何东西,那么你可以适应 the code to strip HTML tags from a text:

#!/usr/bin/perl -w
use strict;
use warnings;

$_ = do { local $/; <DATA> };

# see http://www.perlmonks.org/?node_id=161281
# ALGORITHM:
#   find < ,
#       comment <!-- ... -->,
#       or comment <? ... ?> ,
#       or one of the start tags which require correspond
#           end tag plus all to end tag
#       or if \s or ="
#           then skip to next "
#           else [^>]
#   >
s{
  <               # open tag
  (?:             # open group (A)
    (!--) |       #   comment (1) or
    (\?) |        #   another comment (2) or
    (?i:          #   open group (B) for /i
      (           #     one of start tags
        SCRIPT |  #     for which
        APPLET |  #     must be skipped
        OBJECT |  #     all content
        STYLE     #     to correspond
      )           #     end tag (3)
    ) |           #   close group (B), or
    ([!/A-Za-z])  #   one of these chars, remember in (4)
  )               # close group (A)
  (?(4)           # if previous case is (4)
    (?:           #   open group (C)
      (?!         #     and next is not : (D)
        [\s=]     #       \s or "="
        ["`']     #       with open quotes
      )           #     close (D)
      [^>] |      #     and not close tag or
      [\s=]       #     \s or "=" with
      `[^`]*` |   #     something in quotes ` or
      [\s=]       #     \s or "=" with
      '[^']*' |   #     something in quotes ' or
      [\s=]       #     \s or "=" with
      "[^"]*"     #     something in quotes "
    )*            #   repeat (C) 0 or more times
  |               # else (if previous case is not (4))
    .*?           #   minimum of any chars
  )               # end if previous char is (4)
  (?(1)           # if comment (1)
    (?<=--)       #   wait for "--"
  )               # end if comment (1)
  (?(2)           # if another comment (2)
    (?<=\?)       #   wait for "?"
  )               # end if another comment (2)
  (?(3)           # if one of tags-containers (3)
    </            #   wait for end
    (?i:\3)       #   of this tag
    (?:\s[^>]*)?  #   skip junk to ">"
  )               # end if (3)
  >               # tag closed
 }{}gsx;         # STRIP THIS TAG

print;

__END__
<html><title>remove script, ul</title>
<script type="text/javascript"> 

function getCookies() { return ""; }

</script>
<body>
<ul><li>1
<li>2
<p>paragraph

输出

remove script, ul


1
2
paragraph

注意:此正则表达式不适用于嵌套标签容器,例如:

<!DOCTYPE html>
<meta charset="UTF-8">
<title>Nested &lt;object> example</title>
<body>
<object data="uri:here">fallback content for uri:here
  <object data="uri:another">uri:another fallback
  </object>!!!this text should be striped too!!!
</object>

输出

Nested &lt;object> example

!!!this text should be striped too!!!

Don't parse html with regexs. 使用 html 解析器或构建在它之上的工具,例如,HTML::Parser

#!/usr/bin/perl -w
use strict;
use warnings;

use HTML::Parser ();

HTML::Parser->new(
    ignore_elements => ["script"],
    ignore_tags => ["ul"],
    default_h => [ sub { print shift }, 'text'],
    )->parse_file(\*DATA) or die "error: $!\n";

__END__
<html><title>remove script, ul</title>
<script type="text/javascript"> 

function getCookies() { return ""; }

</script>
<body>
<ul><li>1
<li>2
<p>paragraph

输出

<html><title>remove script, ul</title>

<body>
<li>1
<li>2
<p>paragraph

【讨论】:

    【解决方案2】:

    回复您最后的评论:

    perl -e'$file="<script etc>\nfoo\n</script>bar"; $file =~ s/<script.*script>//gis; print $file'
    

    正如其他人所建议的那样,这似乎确实符合您的要求。不过,我看不出这与您尝试的有什么不同。

    ....

    你能补充一下吗:

    use Data::Dumper;
    $Data::Dumper::Useqq=1;
    print Dumper($file);
    

    在正则表达式之前并给我们结果?

    .....

    宾果游戏:

    您的 $file =~ 列表的第 5 行和第 6 行已经将它们过滤掉了:

    $file =~ s/<\!DOCTYPE(.*)>//gi;
    $file =~ s/<html>//gi;
    $file =~ s/<\/html>//gi;
    $file =~ s/<title>//gi;
    $file =~ s/<\/title>//gi;
    ## Here they come:
    $file =~ s/<script(.*)>//gi;
    $file =~ s/<\/script>//gi;
    $file =~ s/<head>//gi;
    

    【讨论】:

    • 把它放到我的代码中会导致我的输出......完全不存在。我认为它是由使 $file 等于 "//gis;也不删除标签。我完全不明白出了什么问题... :(
    • 在我的回答中添加了一个问题(当我在评论中这样做时会弄得一团糟)
    • 当我输入该代码时,它会打印文本文件(减去正则表达式)。
    • 啊,是的。我在问题中添加了一行($Data::Dumper::Useqq=1;)。对不起。你能再试一次吗?
    • 我尝试了两种方法。首先,我让正则表达式在此代码执行后运行,导致一堵似乎由原始文本文件组成的文本墙,减去许多空格,然后是文件的正则表达式版本(减去我的正则表达式试图修复,当然大声笑)。在第二次试验中,我注释掉了正则表达式,产生了前面提到的相同的墙,然后打印出文本文件,因为它会减去任何正则表达式。
    【解决方案3】:

    我不确定您使用的是哪种编程语言,但假设您使用的是 perl,请尝试将 s 修饰符放在正则表达式的末尾:

    $file =~ /<script type(.*)<\/script>/sgi
    

    /s 修饰符使. 匹配任何字符,包括换行符(通常不包括换行符)


    编辑:对不起,我不擅长 Perl,但我环顾四周,终于意识到前面的 s/ 是用于替换的。在这种情况下,您的正则表达式应该是:

    $file =~ s/<script type(.*)<\/script>/sgi
    

    删除所有内容,包括脚本标签。但是,如果您只想要标签之间的内容,那就是:

    $file =~ s/(<script type="[^"]*"\s*>).*(<\/script>)/$1$2/sgi;
    

    注意斜线之间的$1$2。此文本是替换文本。在这种情况下,我们使用来自捕获组的文本代替原始文本。在您的问题中,您连续使用了两个斜杠(s/&lt;ul(.*)&gt;//gi),这意味着您将整个匹配替换为空字符串。在我看来,您实际上是想用空格(ASCII 20)替换所有内容,例如 s/&lt;ul(.*)&gt;/ /gi


    自您上次编辑后 - 您需要对脚本使用一个正则表达式,因为您不想要内容:

    $file =~ s/(<script type="[^"]*"\s*>).*(<\/script>)/ /sgi;
    

    以及所有其他标签的另一个通用正则表达式:

    $file =~ s/<\/?\s*[^>]+>//sgi
    

    我在这里假设您不想仅限于上面显示的标签,您只想杀死所有 HTML。有一个名为html2text 的*nix 实用程序可以执行此操作。您可能想考虑使用它。

    【讨论】:

    • 哈哈,我也没有注意到丢失的 s ......嗯,这并没有修复它,虽然......我不确定发生了什么......那一点代码应该删除这些标签中的所有内容...此外,对于 perl 中的替换,需要在“sgi”之前有一个额外的 / 以指示您要替换的文本。我也尝试过,但没有运气。悲伤的熊猫。
    • 放入第一个正则表达式会给我一个语法错误,我认为这是由于没有指定应该替换
    • 抱歉,您能在此处粘贴一些代码,以便我查看您正在运行的内容吗?我打开了一个 perl shell,但没有出现语法错误...
    【解决方案4】:

    您将不得不比这更加小心。在this answer 中查看这两种方法。

    【讨论】:

    • 我查看了第二种解决方案,但不幸的是,我在使用 perl 方面非常陌生,并且不确定这段代码实际上是如何工作的。我假设: s{ {}sx; s{ }{}gsx; s{ $style_tag_rx .*? }{}gsix; s{ $script_tag_rx .*? }{}gsix; s{ }{}gsx;是什么实际替换,但老实说我不知道​​这是如何工作的......
    【解决方案5】:

    这个:

    $file =~ s/<div(.*)>//gi;
    

    不会做你所期望的。 '*' 运算符是贪婪的。如果你有这样的一行:

    hello<div id="foo"><b>bar!</b>baz
    

    它会尽可能多地替换,只留下:

    hellobaz
    

    你想要:

    $file =~ s/<div[^>]*>//gi;
    

    $file =~ s/<div.*?>//gi;
    

    【讨论】:

    • 我会尽可能在问题中发布作业!
    • . 不是正则表达式中的运算符,也不是贪婪的。您的意思是后缀量词,它们是某种运算符,默认情况下将最大匹配。
    • @Sheldon:祝你作业顺利。如果它不能按预期工作,只需 1 个正则表达式开始,然后将其余的一个一个添加,观察结果如何变化。总有一天你会掌握窍门的;)