【问题标题】:Join multiple files into one using a key and rearrange the columns using perl.使用键将多个文件合并为一个,并使用 perl 重新排列列。
【发布时间】:2013-11-06 00:24:26
【问题描述】:

如果我尝试读取多个大文件并使用密钥连接它们,我应该采取什么方法。可能有 1 到多个组合,因此一次读取一行适用于我的简单场景。寻求一些指导。谢谢!

use strict;
use warnings;

open my $head, $ARGV[0] or die "Can't open $ARGV[0] for reading: $!";
open my $addr, $ARGV[1] or die "Can't open $ARGV[1] for reading: $!";
open my $phone, $ARGV[2] or die "Can't open $ARGV[2] for reading: $!";
#open my $final, $ARGV[3] or die "Can't open $ARGV[3] for reading: $!";


while( my $line1 = <$head> and my $line2 = <$addr> and my $line3 = <$phone>)
{
        #split files to fields
        my @headValues = split('\|', $line1);

        my @addrValues = split('\|', $line2);

        my @phoneValues = split('\|', $line3);


        # if the key matches, join them
        if($headValues[0]==$addrValues[0] and $headValues[0]==$phoneValues[0])
        {

        print "$headValues[0]|$headValues[1]|$headValues[2]|$addrValues[1]|$addrValues[2]|$phoneValues[1]";

        }

}
close $head;

【问题讨论】:

  • 多大是多大?每个文件有多少字节,多少行?哪些文件(如果有)可以有多行具有相同的键值?
  • 友情提示:use autodie;,然后你就不必做open or die的事情了。
  • 数百万行和高达 16GB 的文件。 head.txt 是独一无二的。除了 head.txt 之外的所有文件都可以有多行具有相同的键。
  • 关于文件内容的更多信息可能会有用
  • (1) 文件是否在第一列排序或聚集? (2) 行的顺序重要吗?或者可以重新排序某些行? (3) 头文件与其他文件相比有多大? (4) 这段代码多久运行一次?一次,还是每天/每周? (5) 是否有特定的硬件限制,例如内存小,硬盘? (6) 如果您可以将您的意图表达为 SQL 查询,这可能会有所帮助。

标签: perl


【解决方案1】:

我不确定它是否正是您正在寻找的,但您是否尝试过 UNIX 命令join? 考虑这两个文件:

x.tsv

001 X1
002 X2
004 X4

y.tsv

002 Y2
003 Y3
004 Y4

命令join x.tsv y.tsv 产生:

002 X2 Y2
004 X4 Y4

也就是说,它将具有相同 ID 的行合并并丢弃其他行(为了简单起见)。

【讨论】:

    【解决方案2】:

    如果我是你,那么我会从这三个文件构建一个 sqlite 数据库,然后使用 sql 检索结果会容易得多。

    我不知道它会有多快,但我认为它比并行读取三个文件要强大得多。 SQlite 可以处理这么多的数据。

    http://perlmaven.com/simple-database-access-using-perl-dbi-and-sql

    SQLite for large data sets?

    #!/usr/bin/perl
    use strict;
    use warnings;
    
    use DBI;
    
    my $dbfile = "sample.db";
    
    my $dsn = "dbi:SQLite:dbname=$dbfile";
    my $user = "";
    my $password = "";
    my $dbh = DBI->connect($dsn, $user, $password, {
    PrintError => 1,
    RaiseError => 1,
    FetchHashKeyName => 'NAME_lc',
    AutoCommit => 0,
    });
    $dbh->do('PRAGMA synchronous = OFF');
    
    my $sql = <<'END_SQL';
    CREATE TABLE t1 (
    id INTEGER PRIMARY KEY,
    c1 VARCHAR(100),
    c2 VARCHAR(100),
    c3 VARCHAR(100),
    c4 VARCHAR(100),
    )
    END_SQL
    
    $dbh->do($sql);
    
    my $sql = <<'END_SQL';
    CREATE TABLE t2 (
    id INTEGER PRIMARY KEY,
    c1 VARCHAR(100),
    c2 VARCHAR(100),
    c3 VARCHAR(100),
    c4 VARCHAR(100),
    )
    END_SQL
    
    $dbh->do($sql);
    
    my $sql = <<'END_SQL';
    CREATE TABLE t3 (
    id INTEGER PRIMARY KEY,
    c1 VARCHAR(100),
    c2 VARCHAR(100),
    c3 VARCHAR(100),
    c4 VARCHAR(100),
    )
    END_SQL
    
    $dbh->do($sql);
    ### populate data
    open my $fh, $ARGV[0] or die "Can't open $ARGV[0] for reading: $!";
    while( my $line = <$fh> ){
        my @cols = split('\|', $line);
        $dbh->do('INSERT INTO t1 (id, c1, c2, c3, c4) VALUES (?, ?, ?)',undef,$col[0],$col[1],$col[2],$col[3]);
    }
    close($fh);
    $dbh->commit();
    open my $fh, $ARGV[1] or die "Can't open $ARGV[1] for reading: $!";
    while( my $line = <$fh> ){
        my @cols = split('\|', $line);
        $dbh->do('INSERT INTO t2 (id, c1, c2, c3, c4) VALUES (?, ?, ?)',undef,$col[0],$col[1],$col[2],$col[3]);
    }
    close($fh);
    $dbh->commit();
    open my $fh, $ARGV[2] or die "Can't open $ARGV[2] for reading: $!";
    while( my $line = <$fh> ){
        my @cols = split('\|', $line);
        $dbh->do('INSERT INTO t3 (id, c1, c2, c3, c4) VALUES (?, ?, ?)',undef,$col[0],$col[1],$col[2],$col[3]);
    }
    close($fh);
    $dbh->commit();
    ### process data
    my $sql = 'SELECT t1.c1, t1.c2, t1.c3, t2.c2, t2.c3, t3.c2 FROM t1,t2,t3 WHERE t1.c1=t2.c1 AND t1.c1=t3.c1 ORDER BY t1.c1';
    my $sth = $dbh->prepare($sql);
    $sth->execute(1, 10);
    while (my @row = $sth->fetchrow_array) {
        print join("\t",@row)."\n";
    }
    
    $dbh->disconnect;
    #unlink($dbfile);
    

    【讨论】:

    • 谢谢!我会试一试。想知道性能会怎样。
    • 我正在尝试用 Cygwin 测试它并得到这个错误: install_driver(SQLite) failed: Can't locate DBD/SQLite.pm in @INC (@INC contains: /usr/lib/perl5 /site_perl/5.14/x86_64-cygwin-threads /usr/lib/perl5/site_perl/5.14 /usr/lib/perl5/vendor_perl/5.14/x86_64-cygwin-threads /usr/lib/perl5/vendor_perl/5.14 /usr/lib /perl5/5.14/x86_64-cygwin-threads /usr/lib/perl5/5.14 .) at (eval 3) line 3. 可能 DBD::SQLite perl 模块尚未完全安装,或者可能是“SQLite”的大写' 不对。可用的驱动程序:DBM、ExampleP、File、Gofer、Proxy、Sponge、mysql。在 useDB.pl 第 12 行
    • 找到了我的答案。需要安装所有缺少的组件并安装:
    • 这允许我们登录 CPAN 提示符:# perl -MCPAN -e shell 一旦你进入 CPAN 提示符:cpan> install DBD::SQLite
    【解决方案3】:

    试图了解您的文件。您有一个包含 head 值的文件(无论是什么),一个包含电话号码的文件,一个包含地址的文件。那是对的吗?每个文件可以有多个head地址电话号码,并且每个文件都以某种方式相互对应。

    您能否举例说明文件中的数据以及它们之间的关系?一旦我对您的数据的实际情况有了更好的了解,我会立即更新我的答案。

    同时,是时候了解references了。引用允许您创建更复杂的数据结构。而且,一旦你理解了引用,你就可以转向面向对象的 Perl,这将真正让你处理你不知道可能的编程任务。

    Perl 引用允许您拥有 散列的散列数组的数组散列的数组数组的散列,当然,该数组或散列中的那些数组或散列本身可以具有数组或散列。也许举个例子会有所帮助。

    假设您有一个按员工编号分配的人员哈希。我假设你的第一个文件是employee_id|name,第二个文件是address|city_state,第三个是home_phone|work_phone

    首先,只需将文件读入数组:

    use strict;
    use warnings;
    use autodie;
    use feature qw(say);
    
    open my $heading_fh, "<", $file1;
    open my $address_fh, "<", $file2;
    open my $phone_fh, "<", $file3;
    
    my @headings = <$heading_fh>;
    chomp @headings;
    close $heading_fh;
    
    my @addresses = <$address_fh>;
    chomp @addresses;
    close $address_fh;
    
    my @phones = <$phone_fh>;
    chomp @phones;
    close $phone_fh;
    

    这将使操作各种数据流变得更加容易。现在,我们可以遍历每一行:

    my %employees;
    for my $employee_number (0..$#headings) {
        my ( $employee_id, $employee_name ) = split /\s*\|\s*/, $employees[$employee_number];
        my ( $address, $city ) = split  /\s*\|\s*/, $phones[$employee_number];
        my ( $work_phone, $home_phone ) = split /\s*\|\s*/, $addresses[$employee_number];
        my $employees{$employee_id}->{NAME} = $employee_name;
        my $employees{$employee_id}->{ADDRESS} = $address;
        my $employess{$employee_id}->{CITY} = $city;
        my $employees{$employee_id}->{WORK} = $work_phone;
        my $employees{$employee_id}->{HOME} = $home_phone;
    }
    

    现在,您有一个名为%employees 的散列,它以$employee_id 为键,散列中的每个条目都是对另一个散列的引用。你有一个散列的散列

    最终结果是由$employee_id 键入的单个数据结构(您的%employees),但每个字段都可以单独访问。 A103 号员工叫什么名字,$employees{A103}-&gt;{NAME}

    代码远未完成。例如,您可能想验证所有初始数组的大小是否相同,如果不是,则死亡:

    if ( ( not $#employees == $#phones ) or ( not $#employees == $#addresses ) ) {
        die qq(The files don't have the same number of entries);
    }
    

    我希望使用 references 和使用更复杂的数据结构的想法可以让事情变得更容易处理。但是,如果您需要更多帮助。发布一个示例,说明您的数据是什么样的。还要解释各个领域是什么以及它们之间的关系。

    Stackoverflow 上有很多帖子在我看来是这样的:

    我的数据如下所示:

    ajdjadd|oieuqweoqwe|qwoeqwe|(asdad|asdads)|adsadsnrrd|hqweqwe
    

    而且,我需要让它看起来像这样:

    @#*()#&&###|@#*@#&)(*&!@!|@#@#&(*&@#
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-08-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-03
      • 1970-01-01
      • 2011-02-15
      • 1970-01-01
      相关资源
      最近更新 更多