我终于知道如何使用正则表达式将一个子字符串替换为另一个子字符串,每个子字符串在字符串中出现的位置。但是我现在需要做的比这要复杂一些。
我必须转换的字符串将具有许多换行符 (') 的实例。如果这些换行符包含在鱼标签中(在"<"和">"之间),我需要用一个简单的空格字符('' )替换它。
但是,如果换行符出现在字符串中的其他任何位置,我需要保留该换行符。
字符串中将有几个地方被括在鱼标签中,有几个地方不是。
有没有办法在 PERL 中做到这一点?
老实说,我不建议使用正则表达式执行此操作。除了永远不应该使用正则表达式解析html之外,使用正则表达式进行负匹配也很痛苦,任何阅读代码的人都会老实说不知道你刚刚做了什么。 另一方面,手动执行此操作真的很容易理解。
此代码假设格式良好的 html 没有从其他标签的定义中开始的标签(否则您必须跟踪所有实例并适当地增加/减少计数),并且它不处理引号字符串内的<或>,这不是最常见的事情。如果你正在做所有这些,我真的建议你使用一个真正的html解析器,其中有很多。或>
显然,如果您不是从文件句柄读取此内容,则循环将遍历一组行(或拆分整个文本的输出,尽管如果您拆分,您将根据内部变量附加 ' ' 或 "",因为它会删除换行符)
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
>
给定:
$ 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+([^>]*>)/12/g"
Line 1
Line 2
< fish tag line 1 and line 2 >
< line 3 >
< fish tag line 4 and line 5 >
我会回应说,这只在有限的情况下有效。请不要养成使用 HTML 正则表达式的一般习惯。
这个解决方案使用 zdim 的数据(谢谢,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 >
(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 (...) { }
的方式,其中循环的主体为空,因此一切都发生在条件中,反复计算直到 false。在我们的例子中,替换一直在条件下进行,直到没有匹配,当循环退出时。
感谢ysth
提出这些要点和1 while ...
解决方案。
所有这些对各种细节和边缘情况(可能还有更多)的必要关注希望能说服您最好找到适合特定任务的 HTML 解析模块。 为此,我们需要更多地了解这个问题。