将字符串添加/删除到模式匹配

时间:2019-03-20 14:51:20

标签: regex perl sed

我需要找到基于模式的列表,并添加或删除其他字符串...

我在文件中有一个URL的xml列表。

在我的文件中,单行显示:

"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"

我的问题:

  1. 在Group1中,我要将“ newwebsite.com”添加到列表中(在列表的末尾)

所以u-l=\"toto.com;tata.com;tutu.com\"变成u-l=\"toto.com;tata.com;tutu.com;newwebsite.com\"

我当然只知道“ Group1”和“ newwebsite.com” ...

  1. 在Group3中,我要从列表中删除“ toto.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的原因,这是一团糟字符,输出与输入永远不匹配...

3 个答案:

答案 0 :(得分:1)

仅通过解析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子项中完成的,它将对组进行迭代,在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解析器。

答案 1 :(得分:0)

我相信这可行。我正在使用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"

答案 2 :(得分:0)

通过gnu sed将您的数据字符串保存在“ d”文件中

sed -E 's/(Group1\\.[^>]+)\"(><)/\1;newwebsite.com"\2/i; s/(Group3\\"[^=]+=\\")toto\.com;/\1/i' d