如何在Perl中进行字母数字排序?

zujrkrfu  于 2022-11-15  发布在  Perl
关注(0)|答案(4)|浏览(258)

我有一个文件,它看起来像这样:

80,1p21
81,19q13
82,6p12.3
83,Xp11.22
84,3pter-q21
86,3q26.33
87,14q24.1-q24.2|14q24|14q22-q24
88,1q42-q43
89,11q13.1
90,2q23-q24
91,12q13
92,2q22.3
93,3p22
94,12q11-q14
95,3p21.1
97,14q24.3
98,2p16.2

我想根据第二列对它们进行排序。第一列也应该相应地改变。当你在Perl中使用'sort'命令时,它不会这样做,因为它说它不是数字。在Perl中有没有一种方法可以按字母数字排序?

3bygqnnd

3bygqnnd1#

如果你读了the documentation for sort,你会发现你不需要在Perl中进行数字排序,你也可以进行字符串比较。

@sorted = sort { $a cmp $b } @unsorted;

但是这仍然会给你留下一个问题,比如19q会排在6p之前,所以你可以写你自己的排序函数,在做比较之前做你想要的任何转换。

@sorted = sort my_complex_sort @unsorted;

sub my_complex_sort {
  # code that compares $a and $b and returns -1, 0 or 1 as appropriate
  # It's probably best in most cases to do the actual comparison using cmp or <=>

  # Extract the digits following the first comma
  my ($number_a) = $a =~ /,(\d+)/;
  my ($number_b) = $b =~ /,(\d+)/;

  # Extract the letter following those digits
  my ($letter_a) = $a =~ /,\d+(a-z)/;
  my ($letter_b) = $b =~ /,\d+(a-z)/;

  # Compare and return
  return $number_a <=> $number_b or $letter_a cmp $letter_b;
}
23c0lvtd

23c0lvtd2#

#!/usr/bin/env perl

use strict;
use warnings;

my @datas   = map { /^(\d+),(\d*)(.*)$/; [$1, $2, $3]; } <DATA>;
my @res     = sort {$a->[1] <=> $b->[1] or $a->[2] cmp $b->[2]} @datas;
foreach my $data (@res) {
    my ($x, $y, $z) = @{$data};
    print "$x,$y$z\n";
}

__DATA__
80,1p21
81,19q13
82,6p12.3
83,Xp11.22
84,3pter-q21
86,3q26.33
87,14q24.1-q24.2|14q24|14q22-q24
88,1q42-q43
89,11q13.1
90,2q23-q24
91,12q13
92,2q22.3
93,3p22
94,12q11-q14
95,3p21.1
97,14q24.3
98,2p16.2
tvmytwxo

tvmytwxo3#

实际上我找到了这个问题的答案。虽然代码看起来有点复杂。

#!/usr/bin/env perl

use strict;  
use warnings;

sub main {   
my $file;  
if (@ARGV != 1) {   
    die "Usage: perl hashofhash_sort.pl <filename>\n";
}   
else {  
    $file = $ARGV[0];   
}  

open(IN, $file) or die "Error!! Cannot open the $file file: $!\n";
my @file = <IN>;
chomp @file;
my ($entrez_gene, $loci, $chr, $band, $pq, $band_num);
my (%chromosome, %loci_entrez);

foreach my $line (@file) {
    if ($line =~ /(\d+),(.+)/) {
        # Entrez genes
        $entrez_gene = $1;

        # Locus like 12p23.4
        $loci = $2;

        if ($loci =~ /^(\d+)(.+)?/) {
            # chromosome number alone (only numericals)
            $chr = $1;
            if ($2) {
                # locus minus chromosome number. If 12p23.4, then $band is p23.4
                $band = "$2";
                if ($band =~ /^([pq])(.+)/) {
                    # either p or q
                    $pq = $1;
                    # stores the numericals. for p23.4, stores 23.4
                    $band_num = $2;
                }

                if (exists $chromosome{$chr}) {
                    if (exists $chromosome{$chr}{$pq}) {
                        push (@{$chromosome{$chr}{$pq}}, $band_num);
                    }
                    else {
                        $chromosome{$chr}{$pq} = [$band_num];
                    }
                }

                else {
                    $chromosome{$chr}{$pq} = [$band_num];
                }
            }
        }
    }
} # End of foreach loop

foreach my $key (sort {$a <=> $b} keys %chromosome) {
    my %seen = ();
    foreach my $key2 (sort {$a cmp $b } keys %{$chromosome{$key}}) {
        my @unique = grep { ! $seen{$_}++ } @{$chromosome{$key}{$key2}};
        my @sorted = sort @unique;
        foreach my $element (@sorted) {
            my $sorted_locus = "$key$key2$element";
            if (exists $loci_entrez{$sorted_locus}) {
                foreach my $element2 (@{$loci_entrez{$sorted_locus}}) {
                        print "$element2,$sorted_locus\n";

                }
            }
        }
    }

}

} # End of main

main();
lmvvr0a8

lmvvr0a84#

在非常普遍的情况下,如何处理相等但写法不同的整数是一个模棱两可的问题,因为可能会有前导零。下面的比较函数(对于sort)允许我们在没有不同整数的情况下考虑字典顺序。这与zsh的数字排序相同。

sub alphanumcmp ($$)
  {
    my (@u,@v);
    if ((@u = $_[0] =~ /^(\d+)/) &&
        (@v = $_[1] =~ /^(\d+)/))
      {
        my $c = $u[0] <=> $v[0];
        return $c if $c;
      }
    if ((@u = $_[0] =~ /^(.)(.*)/) &&
        (@v = $_[1] =~ /^(.)(.*)/))
      {
        return $u[0] cmp $v[0] || &alphanumcmp($u[1],$v[1]);
      }
    return $_[0] cmp $_[1];
  }

例如,可以得到以下排序的元素:

a0. a00. a000b a00b a0b a001b a01. a01b a1. a1b a010b a10b a011b a11b

注1:使用<=>时,假设数字不是太大。
注2:在这个问题中,用户希望对第二列(而不是整个字符串)进行字母数字排序。因此,在这个特定的情况下,比较函数可以只忽略第一列,或者可以使用Schwartzian transform

相关问题