【问题标题】:Add/Delete a string to a pattern matching在模式匹配中添加/删除字符串
【发布时间】:2019-08-11 07:41:59
【问题描述】:

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

我在一个文件中有一个 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"

我的问题:

  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字符,输出永远不会匹配输入...

【问题讨论】:

  • 看起来像 JSON,里面有 XML。如果这是真的,在 Perl 中使用 JSON 和 XML 模块应该会对你有很大帮助。
  • 我知道(正如我所说,我可以使用 perl 模块对所有内容进行排序,但特殊字符总是会修改输出,这就是我想处理全局文件的原因)。
  • 如果您正确使用 XML 和 JSON 解析器,这应该相对简单,正如 choroba 所说。
  • 我没有提到它,但是 json 文件很大(几 MBytes),其中的 xml 字符串包含数千个条目。我将不得不解析所有文件并重新创建一个新文件(使用完全相同的语法)......我看不出它比正则表达式更简单(对于比我更了解正则表达式的人)。而且我仍然确认非 utf-8 字符在您阅读和编写它们时真的很痛苦
  • 如果 XML 数据是真正的 XML,而不是类似于 XML 的东西,那么当它返回时,它仍然意味着相同的东西。 JSON 也是如此。我的经验告诉我,XML 位比 JSON 位更受关注。您最好使用两个解析器的组合,但您至少必须通过 JSON 解析器运行它才能得到所有转义。

标签: regex perl sed


【解决方案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,但我们只是将其视为一堆文本。我们需要&lt;node&gt; 的开始和结束括号作为锚点。我们取出域列表,并对其进行操作。如果有多余的空格或元素的顺序发生变化,则会中断。

代码使用简单的列表操作来处理域列表。

为了轻松删除多个域,这使用了实验性 smartmatch 运算符。你可以用不同的方式实现它,但我很懒。它只适用于某些 Perl 版本,因为这是实验性的。

然后,我们将新列表替换为看起来像 XML 的大字符串,然后用它替换旧列表。我们需要确保没有特殊字符(如点.)进入模式,因此我们使用\Q\E 对其进行转义。

如果还不清楚,我会再说一遍。虽然这适用于您在问题中给出的这组非常具体的参数,但很可能这在您的生产环境中无法完全发挥作用。你将不得不适应它,并且可能经常适应它。

您最好同时使用 JSON 解析器和 XML 解析器。

【讨论】:

  • 谢谢。我试过了,但我有“在模式匹配(m//)中使用未初始化的值 $xml”。我没有收到您对子流程的调用(应该有 2 个参数,但是当您调用它时,在我看来只有一个……)。结果是:group1 中没有添加,group3 中的所有内容都删除了。有关信息,我犯了一个错误:“;”不是我文件中的分隔符,“ ”是分隔符(我已将其放在拆分和连接行中,但没有成功)。
  • 忘记我之前的评论,我在 json 中没有看到第二个级别(所以它是 $hash->{content}->{xml})。我犯了一个错误:在&lt;node n" u-l 中,分隔符不是空格而是“\n”。因此,当我打开文件时,所有 xml 都在一行中,但是当脚本读取它时,似乎“\n”被解释(因此网站与“Group1”不在同一行)。如果它不是同一行,则正则表达式永远不会匹配...
【解决方案2】:

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

【讨论】:

  • 我将时间集中在解决它的上一个答案上。但是我要感谢您,因为您的回答对我也很有用(其他原始文本文件中需要纯正则表达式,因此您的正则表达式可以帮助我更多地了解它在复杂模式匹配中的工作原理)
【解决方案3】:

通过 gnu sed ,您在 'd' 文件中的数据字符串

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-15
    • 2021-05-26
    相关资源
    最近更新 更多