【问题标题】:How to sort by numbers in alpha numeric string?如何按字母数字字符串中的数字排序?
【发布时间】:2019-11-21 14:48:08
【问题描述】:

如果我有一个格式如下的字符串数组:

[1900] ABC 15

如何使用 perl sort 对数组进行排序,使其先按第一个数字排序,然后按第二个数字排序?

perldoc sort 的这个例子似乎是相关的:

my @new = sort {
    ($b =~ /=(\d+)/)[0] <=> ($a =~ /=(\d+)/)[0]
                        ||
                fc($a)  cmp  fc($b)
} @old;

【问题讨论】:

  • 当你说 Perl 时,你应该在上下文中显示一些代码。这个[1900] ABC 15 没有任何意义。
  • 我认为您需要定义自己的排序子例程,然后检查要比较的字符串部分。您可以将第二个数字附加到第一个数字以获取一个数字 (190015) 进行比较。
  • @sin = 只是来自示例中的一个模式,引用自文档。请注意,确实需要使用元素(...)[0],否则&lt;=&gt; 将比较两个列表(即使每个列表只有一个元素),并且由于它强加了我们想要的标量上下文比较每个元素的数量,1 &lt;=&gt; 1
  • @zdim - 啊,我记得。您必须使正则表达式响应 wantarray 以获取列表,当然我不认为 需要列表上下文,因此需要索引。感谢您清除它。
  • 请记住,正如文档所说,这是非常低效的。

标签: regex perl sorting


【解决方案1】:

从文档中提取的示例显示了这个想法:按一个标准进行比较,如果通过cmp&lt;=&gt; equality operators 发现它们相等,然后返回0,则转到下一个标准。

所以在这种情况下,先比较字符串中的第一个数字,然后再比较第二个。

use warnings;
use strict;
use feature 'say';

my @old = ('[1900] ABC 15', '[1900] ABC 5', '[1800] ABC 20'); 

my @new = sort { 
    my ($a1, $a2) = $a =~ /([0-9]+)/g;
    my ($b1, $b2) = $b =~ /([0-9]+)/g;

    $a1 <=> $b1  or $a2 <=> $b2;

} @old;

say for @new;

打印

[1800] ABC 20 [1900] 美​​国广播公司 5 [1900] 美​​国广播公司 15

如果需要按降序排序,则在比较中交换 ab

这可以更有效地完成,首先通过预先计算整个列表的正则表达式,这样它们就不会在每次比较元素时都重新完成。来自 docs 的示例的延续显示了这一点,最后一个版本是 Schwartzian transform

但请记住,此类优化仅适用于较大的数据集,而且对于简单计算,它们的开销也很重要。 以上基本的sort一般就够了。


按照要求讨论了“使用 perl sort”的问题,我想补充一点,也有专门为此目的编写的模块,“自然排序”,因为它们调用它。

Sort::Key::Natural 的示例

use warnings;
use strict;
use feature 'say';

use Sort::Key::Natural qw(natsort);

my @strings = qw(
    19_b_2
    13_z_7
    13_b_7
    20_a_1 
    13_b_5 
);

say for natsort @strings;

打印

13_b_5 13_b_7 13_z_7 19_b_2 20_a_1

首先按数字部分排序,然后按字母顺序排序。没有要求第二种类型,上面的sort 也没有这样做。但是手动排序可以很容易地用标准修改,而 nice 模块当然不能那么灵活(不能放弃字母排序)。


注意  [0-9]\d 匹配,但其他字符也是如此(我被告知还有 360 个),它可以识别 Unicode。 /a 字符集修饰符 不是这种情况,自 5.14 起可用。但这比仅限制\d 具有更广泛的影响。在perlre 中搜索/a。 因此,在这里我使用0-9 来提高精度和少量的效率,并且不限制\s\w 和POSIX 字符类。

【讨论】:

  • 使用 /a 修饰符,\d 仅匹配 [0-9],请参阅:perldoc.perl.org/perlretut.html
  • @Toto 谢谢,这就是我所说的“没有修饰符”。从那时起,我不想详细说明/aa 也需要参与其中,而这是一个旁注。不过最好说清楚,谢谢。
  • @Toto 我确实将此添加到文本中,感谢您的评论。
【解决方案2】:

前段时间我写了一个函数来进行这种排序。 它使用字符串中的所有数字进行数字排序。 我不在乎性能,对不起。希望对您有所帮助。

sub num_sort($$) {
  my ($a,$b)=@_;
  my @sa=reverse grep /./s, split /(\d+)/,$a;
  my @sb=reverse grep /./s, split /(\d+)/,$b;
  for (;; ) {
    last if !@sa || !@sb;
    my $ea=pop @sa;
    my $eb=pop @sb;
    my $rc= ($ea <=> $eb) || ($ea cmp $eb);
    if ($rc) {
      return $rc;
    }
  }
  return @sa <=> @sb;
}

【讨论】:

    猜你喜欢
    • 2021-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-28
    • 1970-01-01
    • 1970-01-01
    • 2011-03-10
    相关资源
    最近更新 更多