【问题标题】:Merging 2 nearly identical functions in perl在 perl 中合并 2 个几乎相同的函数
【发布时间】:2017-12-06 18:46:31
【问题描述】:

我有一个从网站获取一些数据的脚本。数据采用 JSON 格式,该站点提供了一个选项,可以将 JSON 输出“扁平化”为单个 JSON 对象,或将其保留为多个对象。

脚本具有允许将 JSON 数据转换为 YAML(无论是否扁平化)或将其保留为 JSON 格式的选项。

此外,脚本对两种格式的值进行着色。

为了完成着色,我目前有2个函数,一个用于JSON着色,一个用于YAML着色。

着色本身是使用 Term::ANSIColor 实现的,通过搜索和替换标量或数组中的文本,具体取决于数据的输出格式。

我想将其简化为一个函数以减少代码重复,但我不知道如何实现这一点。

要明确,为了清楚起见,这个问题的主要焦点是如何使其中一个着色函数可重用,以便它可以在 YAML 和 JSON 输出上工作。因为搜索模式非常非常相似,替换模式也相同,我觉得应该很容易做到这一点,但我对如何做到这一点持空白。

use JSON;
use YAML::Tiny;

sub colorize_yaml
{
    my $OUTPUT                                                                               = shift;
    my $OPTIONS                                                                              = shift;

    if (ref $OUTPUT eq 'SCALAR')
    {
        foreach (${$OUTPUT})
        {

            # Hide this if debugging is disabled, else show it and color it
            if (!$OPTIONS->{debug})
            {
                s{(statusCode|success|dataExist|verumModelObjectName):\ [a-zA-Z0-9]+\n}
{}gxms;
            }
            else
            {
            s{(statusCode|success|dataExist|verumModelObjectName):}
{$OPTIONS->{color} ? BOLD YELLOW $1 . ':', BOLD GREEN : $1 . ':'}gxmse;
            }

            # Colorize 5 segment flat output
            s{([a-zA-Z0-9]+:)([a-zA-Z0-9]+:)([a-zA-Z0-9]+:)([a-zA-Z0-9]+:)([a-zA-Z0-9]+:\ )}
{$OPTIONS->{color} ? BOLD CYAN $1, BOLD YELLOW $2, BOLD MAGENTA $3, BOLD RED $4, RESET $5: $1 . $2 . $3 . $4 . $5}gxmse;

            # Colorize 4 segment flat output
            s{([a-zA-Z0-9]+:)([a-zA-Z0-9]+:)([a-zA-Z0-9]+:)([a-zA-Z0-9]+:\ )}
{$OPTIONS->{color} ? BOLD CYAN $1, BOLD YELLOW $2, BOLD MAGENTA $3, RESET $4 : $1 . $2 . $3 . $4}gxmse;

            # Colorize 3 segment flat output
            s{([a-zA-Z0-9]+:)([a-zA-Z0-9]+:)([a-zA-Z0-9]+:\ )}
{$OPTIONS->{color} ? BOLD CYAN $1, BOLD YELLOW $2, RESET $3 : $1 . $2 . $3}gxmse;

            # Colorize 2 segment flat output
            s{([a-zA-Z0-9]+:)([a-zA-Z0-9]+:\ )}
{$OPTIONS->{color} ? BOLD CYAN $1, RESET $2 : $1 . $2}gxmse;

            # Colorize values in all output
            s{(:\ )}
{$OPTIONS->{color} ? $1 . BOLD GREEN : $1}gxmse;

            # Reset colors before newlines so that the next line starts with a clean color pattern.
            s{\n}
{$OPTIONS->{color} ? RESET "\n" : "\n"}gxmse;
        }
    }
    else
    {
        pretty_print_error("WARNING: Unable to colorize YAML output\n", $OPTIONS->{color});
        return;
    }

    return;
}

sub colorize_json
{
    my $OUTPUT                                                                               = shift;
    my $OPTIONS                                                                              = shift;

    if (ref $OUTPUT eq 'ARRAY')
    {
        foreach (@{$OUTPUT})
        {
            if ($OPTIONS->{debug})
            {
                s{(statusCode|success|dataExist|verumModelObjectName):}
{$OPTIONS->{color} ? BOLD YELLOW $1 . ':', BOLD GREEN : $1 . ':'}gxmse;
            }
            else
            {
                s{(statusCode|success|dataExist|verumModelObjectName):\ [a-zA-Z0-9]+\n}
{}gxms;
            }

            # Colorize 5 segment flat output
            s{^([a-zA-Z0-9]+:)([a-zA-Z0-9]+:)([a-zA-Z0-9]+:)([a-zA-Z0-9]+:)([a-zA-Z0-9]+:\ .*$)}
{$OPTIONS->{color} ? BOLD CYAN $1, BOLD YELLOW $2, BOLD MAGENTA $3, BOLD RED, $4, RESET $5: $1 . $2 . $3 . $4 . $5}gxmse;

            # Colorize 4 segment flat output
            s{^([a-zA-Z0-9]+:)([a-zA-Z0-9]+:)([a-zA-Z0-9]+:)([a-zA-Z0-9]+:\ )}
{$OPTIONS->{color} ? BOLD CYAN $1, BOLD YELLOW $2, BOLD MAGENTA $3, RESET $4 : $1 . $2 . $3 . $4}gxmse;

            # Colorize 3 segment flat output
            s{^([a-zA-Z0-9]+:)([a-zA-Z0-9]+:)([a-zA-Z0-9]+:\ )}
{$OPTIONS->{color} ? BOLD CYAN $1, BOLD YELLOW $2, RESET $3 : $1 . $2 . $3}gxmse;

            # Colorize 2 segment flat output
            s{^([a-zA-Z0-9]+:)([a-zA-Z0-9]+:\ )}
{$OPTIONS->{color} ? BOLD CYAN $1, RESET $2 : $1 . $2}gxmse;

            # Colorize values in all output
            s{(:\ )}
{$OPTIONS->{color} ? $1 . BOLD GREEN : $1}gxmse;

            # Reset colors before newlines so that the next line starts with a clean color pattern.
            s{$}
{$OPTIONS->{color} ? RESET '' : ''}gxmse;
        }
    }
    else
    {
        pretty_print_error("WARNING: Unable to colorize JSON output.\n", $OPTIONS->{color});
        return;
    }

    return;
}

JSON 转换为 YAML

---
message: Success
ObjectList:
  -
    assetName: xxxxxxxx
    backupAsset:
      -
        backupFlag: xxxxxxxx
        fullyCertified: xxxxxxxx

扁平化的 JSON 转换为 YAML

---
message: Success
verumObjectList:
  -
    assetName: xxxxxxxx
    backupAsset:backupFlag: xxxxxxxx
    backupAsset:fullyCertified: xxxxxxxx

JSON(JSON格式的数据被脚本剥离成纯文本)

assetName: xxxxxxxx
backupFlag: xxxxxxxx
fullyCertified: xxxxxxxx
message: Success

扁平化 JSON(JSON 格式的数据被脚本剥离为纯文本)

assetName: xxxxxxxx
backupAsset:backupFlag: xxxxxxxx
backupAsset:fullyCertified: xxxxxxxx
message: Success

虽然我确实需要稍微调整代码,但正确的答案将授予 @zdim。

我在下面发布我的更新代码。

use JSON;
use YAML::Tiny;

sub colorize_output
{
    my $OUTPUT   = shift;
    my $OPTIONS  = shift;

    my $RE_START = $EMPTY;
    my $RE_END   = q{\ };

    if (ref $OUTPUT eq $EMPTY)
    {   
        pretty_print_error("WARNING: Unable to colorize output.\n", 
            $OPTIONS->{color});
        return;
    }   
    elsif (ref $OUTPUT eq 'ARRAY')
    {   
        $RE_START = q{^};
        $RE_END   = q{\ .*};
    }   

    my $ANCHOR    = q{[a-zA-Z0-9]+:};
    my $PATTERN   = qq{($ANCHOR)};

    Readonly my $SEGMENT_LIMIT => 4;

    my $VERUM_RE = qr{(statusCode|success|dataExist|verumModelObjectName):}xms;

    my ($SEGMENT_2PART_RE, $SEGMENT_3PART_RE, $SEGMENT_4PART_RE, $SEGMENT_5PART_RE)
        = map { 
            qr{$RE_START}xms . ($PATTERN x $ARG) . qr{($ANCHOR$RE_END)}xms 
        } 1..$SEGMENT_LIMIT;

    foreach ((ref $OUTPUT eq 'SCALAR')?${$OUTPUT}:@{$OUTPUT})
    {   

        # Hide this if debugging is disabled, else show it and color it
        if (!$OPTIONS->{debug})
        {   
            s{$VERUM_RE\ [a-zA-Z0-9]+}{}gxms;
        }   
        else
        {   
            s{$VERUM_RE}
             {$OPTIONS->{color} ? BOLD YELLOW $1 . ':', BOLD GREEN : $1 . ':'}gxmse;
        }   

        # Colorize sections in flat output
        if ($OPTIONS->{color})
        {   
            s{$SEGMENT_5PART_RE}
             {BOLD CYAN $1, BOLD YELLOW $2, BOLD MAGENTA $3, BOLD RED $4, RESET $5}gxmse;
            s{$SEGMENT_4PART_RE}
             {BOLD CYAN $1, BOLD YELLOW $2, BOLD MAGENTA $3, RESET $4}gxmse;
            s{$SEGMENT_3PART_RE}
             {BOLD CYAN $1, BOLD YELLOW $2, RESET $3}gxmse;
            s{$SEGMENT_2PART_RE}
             {BOLD CYAN $1, RESET $2}gxmse;

            # Colorize values in all output
            s{(:\ )}{$1 . BOLD GREEN}gxmse;

            # Reset colors before newlines or next entry in the list so that
            # the next line starts with a clean color pattern.
            s{(\n|$)}{RESET $1}gxmse;
        }   
    }   

    return;
}   

【问题讨论】:

  • 我们需要两种格式的示例数据。如果您真的编写了一个单元测试并将其包含在内,那将是最好的。有关如何为着色内容编写单元测试的示例,请随时向my tests of the module Dancer2::Logger::Console::Colored 借用。
  • 如果任务是为 YAML 着色,您还可以查看YAML::PP(免责声明:来自我自己)。它可以突出显示 YAML,并且由于 YAML(几乎)是 JSON 的超集,它也可以为 JSON 着色。但我认为目前的突出显示与您想要的不同。
  • @tinita 不幸的是,运行代码和下载数据的机器没有外部网络访问权限,只能访问内部和我们工程师构建的特定存储库。因此,我正在使用 YAML::Tiny,但无法从 CPAN 或其他地方安装 YAML::PP。 :-(
  • @simbabque 我会研究单元测试。我将以所有 4 种格式(json、扁平化 json、json 转换为 yaml 和扁平化 json 转换为 yaml)发布示例数据——很快
  • 好的。伟大的。我还有大约一个半小时的火车车程。 :)

标签: perl code-duplication


【解决方案1】:

这回答了如何在没有更广泛的上下文的情况下重构这些函数的问题。

一个区别是输入:它要么是标量引用,要么是数组引用。

涉及更多的另外两个区别在于正则表达式:arrayref 模式是锚定的,它们的最后一个字母数字模式以\ .*$ 结尾,而 scalarref 模式没有锚定,它们的最后一个匹配以转义空格结尾。

最后,如果$OPTIONS->{color} 为假,那么在所有情况下,整个模式都会被自己替换;所以变量不会改变。那么这个条件应该被拉出来了。

sub colorize_yaml_json {
    my ($OUTPUT, $OPTIONS) = @_;

    my $anchor = '';
    my $last   = qr{\ };
    my @iter_refs;

    if    (ref $OUTPUT eq 'SCALAR') { @iter_refs = $$OUTPUT }
    elsif (ref $OUTPUT eq 'ARRAY')  { 
        @iter_refs = @$OUTPUT;
        $anchor = qr{^};
        $last   = qr{\ .*$};
    }
    else {
        pretty_print_error(...);
        return;
    }

    my $anc  = qr{[a-zA-Z0-9]+:};  # alphanumeric with colon
    my $patt = qr{($anc)};

    my ($seg2_re, $seg3_re, $seg4_re, $seg5_re) = map { 
        qr/$anchor/ . ($patt x $_) . qr/($anc$last)/ 
    } 1..4;

    foreach (@iter_refs) {
        if ($OPTIONS->{debug}) {
            ...
        }        
        if ($OPTIONS->{color}) {
            s{$seg5_re}{BOLD CYAN $1, ... }gxmse;
            ...
        }
    }
    return 1;
}

map 组合了四种情况的整个模式,通过将字母数字(与:)模式$patt 堆叠所需的2-5 次,使用x N1..4,然后附加最后一个图案。

令人不快的复杂情况是需要捕获每个基本模式$anc

我只能在我的模型数据上对此进行测试,所以请仔细检查(一如既往!)。

还有另一个问题是如何最好地处理整个场景,但这不是问题所在,因此没有足够的信息来处理它而无需过多猜测。

【讨论】:

  • @Speeddymon 最初的代码没有像你的问题那样做,更正了。
  • 刚开会回来,会试试这个。谢谢!
  • 在玩了一些之后,发现map 函数正在正确编译变量,但没有产生所需的输出。我没有用map 声明4 个$seg?_re 变量,而是将它们声明如下:my $seg2_re = qr{$ANCHOR${patt}($anc$LAST)}xms; my $seg3_re = qr{$ANCHOR${patt}${patt}($anc$LAST)}xms; my $seg4_re = qr{$ANCHOR${patt}${patt}${patt}($anc$LAST)}xms; my $seg5_re = qr{$ANCHOR${patt}${patt}${patt}${patt}($anc$LAST)}xms;
  • @Speeddymon 好的,感谢您的反馈!在我有限的工作测试中,显然我把它推得太远了。将修复或以其他方式更新(如果无法利用map,请一一编写)。 (需要立即运行)
  • 我很小心,将更新后的代码添加到我的 OP 中,并为您提供了答案。感谢您的帮助。这实际上只是需要了解您可以s///的匹配部分中使用变量名的问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-09-16
  • 1970-01-01
  • 1970-01-01
  • 2021-12-10
  • 2018-03-04
  • 2013-08-14
  • 1970-01-01
相关资源
最近更新 更多