我需要找到一个基于模式的列表,并添加或删除其他字符串......
我在文件中有一个xml的URL列表。
在我的文件中,在一行中,我有这个:
"xml" : "SOMESTUFFWEDONOTCARE<node n=\"Group1\" u-l=\"toto.com;tata.com;tutu.com\"></node><node n=\"Group2\" u-l=\"bobo.com;baba.com\"></node><node n=\"Group3\" u-l=\"toto.com;papa.com;pepe.com;pupu.com\"></node>SOMESTUFFWEDONOTCARE"
我的问题:
所以u-l=\"toto.com;tata.com;tutu.com\"
成为u-l=\"toto.com;tata.com;tutu.com;newwebsite.com\"
当然我唯一知道的就是“Group1”和“newwebsite.com”......
所以u-l=\"toto.com;papa.com;pepe.com;pupu.com\"
成为u-l=\"papa.com;pepe.com;pupu.com\"
它不能从Group1中删除“toto.com”,我认为我不知道“toto.com”在我的Group3列表中的位置(可能是位置1到N)。
解决方案可以是perl代码(作为文件处理程序处理文件)或perl代码中的“sed”(直接使用文件)。我不想将xml放入哈希并对其进行处理(我已经尝试过并且基本上它可以正常工作但是当我们将所有内容再次放入文件中时,由于带有重音字符,换行符或非utf-8,它会变得一团糟字符,输出永远不会匹配输入...
这可以通过解析JSON来实现。我怀疑在不解析JSON的情况下这样做是个好主意。
我编写了一个实现,可以修改组,添加以及从中删除域。你需要记住,用正则表达式改变XML内部的东西总是很脆弱和天真。它会很容易打破,因为它依赖于正确的顺序。它不了解XML标记内的属性,甚至不知道标记本身。这只是一堆文字。
话虽如此,让我们先来看一下配置和实际的通话。
use strict;
use warnings;
use JSON;
my $json =
q[{"xml" :"SOMESTUFFWEDONOTCARE<node n=\"Group1\" u-l=\"toto.com;tata.com;tutu.com\"></node><node n=\"Group2\" u-l=\"bobo.com;baba.com\"></node><node n=\"Group3\" u-l=\"toto.com;papa.com;pepe.com;pupu.com\"></node>SOMESTUFFWEDONOTCARE"}];
my $hash = decode_json $json;
$hash->{xml} = process(
$hash->{xml} => {
"Group1" => {add => [qw/newwebsite.com/]},
"Group3" => {remove => [qw/toto.com/]}
},
);
print encode_json($hash);
第一个假设是你给我们的数据字符串看起来像JSON,实际上是JSON,并且用反斜杠转义是逐字的。如果更改,则所有代码都会中断。
这里的配置允许你说你想要从一个组中的add
和/或remove
域。
这是在process
sub中完成的,它将迭代组,在XML字符串中找到第一个匹配项并处理它。这假设整个XML文档在一行中。如果有换行符,则会中断。
这是完整的功能。
sub process {
my ($xml, $args) = @_;
foreach my $group (keys %$args) {
if ($xml =~ m/<node n="\Q$group\E" u-l="([^"]+)">/) {
my $existing_list = $1;
my @items = split /;/, $existing_list;
# remove items from the list
if (exists $args->{$group}->{remove}) {
no warnings 'experimental';
my @remove = @{$args->{$group}->{remove}};
@items = grep { not $_ ~~ @remove } @items;
}
# add new items to the list
if (exists $args->{$group}->{add}) {
push @items, @{$args->{$group}->{add}};
}
# serialise the list and stick it back in
# need the "" as an anchor
my $new_list = join ';', @items;
$xml =~ s/"(\Q$existing_list\E)"/"$new_list"/;
}
}
return $xml;
}
请记住,虽然这看起来像XML,但我们将其视为一堆文本。我们需要<node>
的开始和结束支架作为锚点。我们抓住域名列表并操纵它。如果有额外的空格或元素的顺序发生变化,则会中断。
该代码使用简单的列表操作来处理域列表。
为了便于删除多个域,这使用了实验性的smartmatch运算符。你可以用不同的方式实现它,但我很懒。它只适用于某些Perl版本,因为这是实验性的。
然后,我们将新列表重新放入看起来像XML的大字符串中,用它替换旧的列表。我们需要确保没有特殊字符(比如点.
)将它放入模式中,因此我们使用\Q
和\E
来逃避它。
如果还不清楚,我会再说一遍。虽然这适用于您在问题中提供的这组非常具体的参数,但这可能无法完全适合您的生产。您将不得不对其进行调整,并可能经常对其进行调整。
您可能最好同时使用JSON解析器和XML解析器。
我相信这有效。我正在使用perl正则表达式替换技术。我希望我没有误解这个问题。
my $line = '"xml" : "SOMESTUFFWEDONOTCARE<node n=\"Group1\" u-l=\"toto.com;tata.com;tutu.com\"></node><node n=\"Group2\" u-l=\"bobo.com;baba.com\"></node><node n=\"Group3\" u-l=\"toto.com;papa.com;pepe.com;pupu.com\"></node>SOMESTUFFWEDONOTCARE"';
my $new_word = "newwebsite.com";
my $remove_word = "toto.com";
print $line;
$line =~ s/(.+)\\\"(Group1\\\" u-l=\\\".+.com)(\\\"\>\<\/node\>\<node n=\\\"Group2.+)(Group3\\\" u-l=\\\".+.com)(.+)/$1.$2.';'.${new_word}.$3.'***'.rm_string($4,$remove_word).$5/e;
print("\n\n$line");
sub rm_string{
$string = shift;
$remove_string=shift;
$string =~ s/$remove_string;?//;
$string =~ s/;$//;
return($string);
}
替换后的结果 -
"xml" : "SOMESTUFFWEDONOTCARE<node n=Group1\" u-l=\"toto.com;tata.com;tutu.com;newwebsite.com\"></node><node n=\"Group2\" u-l=\"bobo.com;baba.com\"></node><node n=\"***Group3\" u-l=\"papa.com;pepe.com;pupu.com\"></node>SOMESTUFFWEDONOTCARE"
通过gnu sed,你的数据字符串在'd'文件中
sed -E 's/(Group1\\.[^>]+)\"(><)/\1;newwebsite.com"\2/i; s/(Group3\\"[^=]+=\\")toto\.com;/\1/i' d