【问题标题】:Perl - to create and sort hash to produce a sorted outputPerl - 创建和排序哈希以产生排序输出
【发布时间】:2021-09-20 14:49:31
【问题描述】:

所以我有这个脚本,它将数据抓取到网站,它获取和下载 CSV,并逐行处理 CSV 并将其转换为 TSV,一旦完成,TSV 文件将转换为 HTML 文件。我已经完成了其余的工作,但是我得到的输出有些混乱,脚本转到源站点上的不同表格页面并下载动态生成的 CSV 文件;然后将该 CSV 文件转换为 TSV 文件,然后我们将其转换为 HTML。 CSV 文件似乎按返回的每一行的第一列排序,但不基于同一行中的任何其他列。因此,第一列值相同的条目可能会从一次下载到同一文件的下一次下载混为一谈。

仅按第一列排序的直观表示,后面是表示列数据的数字:

第一次下载: 1-1 1-2 1-3 2-1 2-2 2-3 3-1 3-2 3-3

第二次下载: 1-1 1-3 1-2 2-2 2-1 2-3 3-3 3-2 3-1

所以我想到的过程是这样的,从源下载 CSV 文件,然后对该 CSV 文件中的行进行排序,以便在编写 TSV 或 HTML 之前将它们标准化以便相互比较文件。这应该允许对更新的数据文件进行准确比较。但我不知道该怎么做我的逻辑是这样的

所以在将 CSV 文件处理为 TSV 文件之前,我会将函数放在 1. 和 2. 之间,我希望 CSV 的内容已经排序。

所以我的脚本是这样的

 my $download_dir_link ="C:/Users/jabella/Downloads";
unlink("$download_dir_link/Product Classification List.csv");

            #CHECK IF CSV FILE DOWNLOAD IS FINISHED
            my $complete_download_flag = 0;
            
            while($complete_download_flag == 0)
            {
                my @download_directory = read_dir($download_dir_link);
                foreach my $downloaded_file (@download_directory)
                {
                    if($downloaded_file =~ /\QProduct Classification List.csv\E/sgi)
                    {
                        $complete_download_flag = 1;
                    }
                }
                sleep(5);
            }
            #SORTED CONTENTS OF CSV BEFORE CONVERSION
            print "sORTING csv content...\n";
            
            #CONVERT CSV TO TSV
            print "Converting csv to tsv...\n";
            my $csv = Text::CSV->new ({ binary => 1 });
            my $tsv = Text::CSV->new ({ binary => 1, sep_char => "\t", eol => "\n"});
            
            open my $infh,  "<:encoding(utf8)", "$download_dir_link/Product Classification List.csv";
            open my $outfh, ">:encoding(utf8)", "Product Classification List.tsv";
            
            while (my $row = $csv->getline ($infh)) 
            {
                $tsv->print ($outfh, $row);
            }
            close($infh);
            close($outfh);
            
            my $tsv_content = "";
            open(my $fh, '<', "Product Classification List.tsv");
            while (<$fh>) 
            {
                $tsv_content = $tsv_content.$_;
            }
            close($fh);
            print "Conversion complete! cleaning tsv content...\n";
            #CLEAN TSV CONTENT
            

            $tsv_content =~ s/(.*?)\t"(.*?)"\t"(.*?)"\t"(.*?)"\t(.*?)\t"(.*?)"\t(.*)/<tr><th>$1<\/th><th>$2<\/th><th>$3<\/th><th>$4<\/th><th>$5<\/th><th>$6<\/th><th>$7<\/th><\/tr>/gi;
            $tsv_content =~ s/"?(.*?)"?\t"?(.*?)"?\t"?(.*?)"?\t"?(.*?)"?\t"?(.*?)"?\t"?(.*?)"?\t"?(.*?)"?\n/<tr><td>$1<\/td><td>$2<\/td><td>$3<\/td><td>$4<\/td><td>$5<\/td><td>$6<\/td><td>$7<\/td><\/tr>\n/gi;
            $tsv_content =~ s/\"{2}/\"/sgi;
            $tsv_content =~ s/(<\/tr>)\n?"/$1/sgi;
            $tsv_content =~ s/\s{2,}/ /sgi;
            $tsv_content =~ s/.*?(<tr>)/$1/si;
            $tsv_content = "<table>\n$tsv_content</table>";
            
            $classification =~ s/_//sgi;
            
            if(exists $existing_index_hash{$doc_uid."_pind.html"})
            {
                if($existing_index_hash{$doc_uid."_pind.html"} ne $tsv_content)
                {
                    $changed_flag = "1";
                    $updated_files = $updated_files."-$classification\n";
                    print "Updated: $classification\n";

希望有人能帮我解决这个问题,谢谢

【问题讨论】:

  • 您发布的大部分代码与问题无关。
  • 另外,你真的需要哈希吗?如果要保持原始顺序,请使用数组。
  • 我展示的是我所做的功能,我现在缺少的是在将CSV文件转换为TSV文件之前将CSV文件的内容排序为升序的功能
  • 我只是想到那个,我真的不知道如何创建那个函数。 :( 所以任何帮助都会做
  • 没有示例 CSV,我无法运行您的代码来使用它。

标签: sorting perl hash


【解决方案1】:

这是一个简单的脚本,它加载指定为参数的 CSV 文件并按前两列排序输出。

#!/usr/bin/perl
use warnings;
use strict;

use Text::CSV_XS;

my $csv = 'Text::CSV_XS'->new({binary => 1, auto_diag => 1});

open my $in, '<', shift or die $!;
my @rows;
while (my $row = $csv->getline($in)) {
    push @rows, $row;
}

# Here the sorting happens. Compare the first column,
# if the values are the same, compare the second column.
@rows = sort { $a->[0] cmp $b->[0] || $a->[1] cmp $b->[1] } @rows;

$csv->say(*STDOUT, $_) for @rows;

您可以使用以下内容按所有列排序(但它将值作为字符串进行比较,它不适用于数字):

sub by_all {
    my ($n, $A, $B) = @_;
    $A->[$n] cmp $B->[$n]
        || $n < $#$A && by_all($n + 1, $A, $B)
}

sort { by_all(0, $a, $b) } @rows;

为了让它也适用于数字,你可以让 Perl 猜测什么是数字:

use Scalar::Util qw{ looks_like_number };

sub by_all {
    my ($n, $A, $B) = @_;
    (looks_like_number($A->[$n])
        ? $A->[$n] <=> $B->[$n]
        : $A->[$n] cmp $B->[$n]
    ) || $n < $#$A && by_all($n + 1, $A, $B)
}

【讨论】:

  • 我无法运行你的脚本,例如read_dir 未在任何地方声明。因此,我无法测试如何使其工作。另一方面,您可以按原样运行我的脚本,它会起作用。
  • 重点是,您不会逐行读取 CSV 文件并打印出来。您将其读入数组,对数组进行排序,然后将排序后的数组打印到输出 TSV。
  • 我的示例输出到*STDOUT,但您可以改用$outfh
  • 能否请您显示我的代码未按预期排序的 2 行?它似乎对我有用。
  • 是的,我的代码按前两列排序。如果您想要更多,只需添加|| $a-&gt;[2] cmp $a-&gt;[2] 等。
猜你喜欢
  • 2012-07-28
  • 2013-09-21
  • 2018-12-17
  • 1970-01-01
  • 2010-10-20
  • 2013-05-25
  • 2012-01-08
  • 2012-11-27
  • 1970-01-01
相关资源
最近更新 更多