PERL在鱼括号内放置换行符,同时保留其他换行符不变?

时间:2016-07-13 17:20:24

标签: string perl

我终于知道如何使用正则表达式将一个子字符串替换为字符串中出现的每个子字符串。但我现在需要做的是比这复杂一点。

我必须转换的字符串将包含许多换行符('\ n')的实例。如果这些换行符包含在fish-tags中(在'<'和'>'之间),我需要用简单的空白字符('')替换它。

但是,如果换行符出现在字符串中的任何其他位置,我需要单独保留该换行符。

字符串中有几个地方用fish-tags括起来,有几个地方没有。

有可能在PERL中执行此操作吗?

4 个答案:

答案 0 :(得分:2)

老实说,我不推荐用正则表达式来做这件事。除了你不应该用正则表达式解析html这一事实之外,与正则表达式进行负面匹配也很痛苦,任何阅读代码的人都会真的不知道你刚刚做了什么。另一方面,手动操作非常容易理解。

此代码假设格式良好的html没有在其他标记的定义内部开始标记(否则您必须跟踪所有实例并适当地递增/递减计数)并且它不处理<或者>引用的字符串里面不是最常见的字符串。如果您正在做所有我真正建议您使用真正的HTML解析器,那么它们就有很多。

显然,如果您不是从文件句柄中读取此内容,则循环将遍历一系列行(或分割整个文本的输出,但您可能会添加'''&#39 ;或者" \ n"取决于内部变量,如果你拆分,因为它会删除换行符)

use strict;
use warnings;

# Default to being outside a tag
my $inside = 0;

while(my $line = <DATA>) {
  # Find the last < and > in the string
  my ($open, $close) = map { rindex($line, $_) } qw(< >);
  # Update our state accordingly.
  if ($open > $close) {
    $inside = 1;
  } elsif ($open < $close) {
    $inside = 0;
  }
  # If we're inside a tag change the newline (last character in the line) with a space. If you instead want to remove it you can use the built-in chomp.
  if ($inside) {
    # chomp($line);
    substr($line, -1) = ' ';
  }
  print $line;
}

__DATA__
This is some text
and some more
<enclosed><a
 b
 c
> <d
 e
 f
>
<g h i



>

答案 1 :(得分:0)

(X)HTML / XML shouldn't be parsed with regex。但是,由于这里没有给出问题的描述是一种方法。希望它能说明这是多么棘手和复杂。

您可以匹配换行符本身。连同有关换行符的详细信息

use warnings;
use strict;

my $text = do {  # read all text into one string
   local $/;
   <DATA>;
};

1 while $text =~ s/< ([^>]*) \n ([^>]*) >/<$1 $2>/gx;

print $text;

__DATA__
start < inside tags> no new line
again <inside, with one nl  
> out
more <inside, with two NLs

     and more text
>

打印

start < inside tags> no new line
again <inside, with one nl   > out
more <inside, with two NLs      and more text  >

否定字符类 [^>]>以外的任何内容匹配,可选和*的任意次数匹配,最多\n 。然后是\n之后的另一个这样的模式,直到结束>/x修饰符允许内部空格,以提高可读性。我们还需要考虑两个特殊情况。

  • \n内可能有多个<...>while循环是一个干净的解决方案。

  • <...>可能有多个\n/g就是1 while ...

while (...) { }成语是写1 while ...的另一种方式,其中循环体是空的,所以一切都在条件中发生,重复评估直到错误。在我们的例子中,替换继续在条件中完成,直到循环退出时没有匹配。

感谢ysth提出这些积分和var badwordlist = new Array("blue", "ass", "drugs", "aciphex", "nude"); alert((new RegExp("blue", "i")).test("[" + badwordlist.join("][") + "]")); alert((new RegExp("greenblue", "i")).test("[" + badwordlist.join("][") + "]")); alert((new RegExp("ass", "i")).test("[" + badwordlist.join("][") + "]")); alert((new RegExp("class", "i")).test("[" + badwordlist.join("][") + "]")); 解决方案。

对各种细节和边缘情况(其中可能有更多)的所有这些必要的关注有希望说服你最好找到适合特定任务的HTML解析模块。为此,我们需要更多地了解这个问题。

答案 2 :(得分:0)

假设:

$ echo "$txt"
Line 1
Line 2
    < fish tag line 1
 and line 2 >

< line 3 >

    < fish tag line 4
 and line 5 >

你可以这样做:

$ echo "$txt" | perl -0777 -lpe "s/(<[^\n>]*)\n+([^>]*>)/\1\2/g"
Line 1
Line 2
    < fish tag line 1 and line 2 >

< line 3 >

    < fish tag line 4 and line 5 >

我会回应说这只适用于有限的情况。请不要养成使用HTML正则表达式的一般习惯。

答案 3 :(得分:0)

此解决方案使用zdim's data(谢谢,zdim)

我更喜欢将可执行文件替换为tr///运算符的非破坏性选项

此解决方案查找包含在尖括号<...>中的所有字符串,并将每个字符串中的所有换行符更改为单个空格

请注意,通过编写

来允许包含任何字符的引用子字符串很简单
$data =~ s{ ( < (?: "[^"]+" | [^>] )+ > ) }{ $1 =~ tr/\n/ /r }gex;
use strict;
use warnings 'all';
use v5.14;  # For /r option

my $data = do {
    local $/;
    <DATA>;
};

$data =~ s{ ( < [^<>]+ > ) }{ $1 =~ tr/\n/ /r }gex;

print $data;


__DATA__
start < inside tags> no new line
again <inside, with one nl  
> out
more <inside, with two NLs

     and more text
>

输出

start < inside tags> no new line
again <inside, with one nl       > out
more <inside, with two NLs           and more text     >