Por*_*ine 4 bash array csv read
File.tsv
是一个有 7 列的制表符分隔文件:
cat File.tsv
1 A J 1
2 B K N 1
3 C L O P Q 1
Run Code Online (Sandbox Code Playgroud)
以下读取File.tsv
7 列制表符分隔文件,并将条目存储在数组 A 中。
while IFS=$'\t' read -r -a D; do
A=("${A[@]}" "${D[i]}" "${D[$((i + 1))]}" "${D[$((i + 2))]}" "${D[$((i + 3))]}" "${D[$((i + 4))]}" "${D[$((i + 5))]}" "${D[$((i + 6))]}")
done < File.tsv
nA=${#A[@]}
Run Code Online (Sandbox Code Playgroud)
while IFS=$'\t' read -r -a D; do
A=("${A[@]}" "${D[i]}" "${D[$((i + 1))]}" "${D[$((i + 2))]}" "${D[$((i + 3))]}" "${D[$((i + 4))]}" "${D[$((i + 5))]}" "${D[$((i + 6))]}")
done < File.tsv
nA=${#A[@]}
Run Code Online (Sandbox Code Playgroud)
tsv 文件中的某些条目为空,但在读取文件时会跳过空值。
是 tsv 文件,空值前后都有制表符。
读取空值并将其存储在数组中。
cas*_*cas 10
正如我在评论中所说,这不是 shell 脚本的工作。bash(和类似的 shell)用于协调其他程序的执行,而不是用于处理数据。
改用任何其他语言 - awk、perl 和 python 都是不错的选择。这将是更容易写,更容易阅读和维护,以及多快。
下面是一个示例,说明如何将文本文件读入 中的哈希数组 (AoH) perl
,然后在各种打印语句中使用这些数据。
AoH 是一种数据结构,正如它的名字所说的那样 - 一个数组,其中每个元素都是一个关联数组(又名哈希)。
顺便说一句,这也可以使用数组数组 (AoA) 数据结构(也称为列表列表或 LoL)来完成,但是能够通过字段名称访问字段而不必记住字段编号很方便.
您可以在 perl 随附的 Perl Data Structures Cookbook 中阅读有关 perl 数据结构的更多信息。运行man perldsc
或perldoc perldsc
。你可能还需要阅读perllol
和perlreftut
太。而perldata
如果你不熟悉Perl变量(“ Perl有三种内建的数据类型:标量,标量数组和标量的关联数组,被称为哈希” A‘标’是任何单一的价值,就像一个数或字符串或对另一个变量的引用)
Perl 附带了大量文档和教程 - 运行man perl
概述。包含的 perl 文档大约有 14MB,因此它通常位于单独的包中,以防您不想安装它。在 debian 上:apt install perl-doc
. 此外,每个库模块都有自己的文档。
#!/usr/bin/perl -l
use strict;
# Array to hold the hashes for each record
my @data;
# Array of field header names. This is used to insert the
# data into the %record hash with the right key AND to
# ensure that we can access/print each record in the right
# order (perl hashes are inherently unordered so it's useful
# and convenient to use an indexed array to order it)
my @headers=qw(SlNo Artist VideoTitle VideoId TimeStart TimeEnd VideoSpeed);
# main loop, read in each line, split it by single tabs, build into
# a hash, and then push the hash onto the @data array.
while (<>) {
chomp;
my %record = ();
my @line = split /\t/;
# iterate over the indices of the @line array so we can use
# the same index number to look up the field header name
foreach my $i (0..$#line) {
# insert each field into the hash with the header as key.
# if a field contains only whitespace, then make it empty
($record{$headers[$i]} = $line[$i]) =~ s/^\s+$//;
}
push @data, \%record ;
}
# show how to access the AoH elements in a loop:
print "\nprint \@data in a loop:";
foreach my $i (0 .. $#data) {
foreach my $h (@headers) {
printf "\$data[%i]->{%s} = %s\n", $i, $h, $data[$i]->{$h};
}
print;
}
# show how to access individual elements
print "\nprint some individual elements:";
print $data[0]->{'SlNo'};
print $data[0]->{'Artist'};
# show how the data is structured (requires Data::Dump
# module, comment out if not installed)
print "\nDump the data:";
use Data::Dump qw(dd);
dd \@data;
Run Code Online (Sandbox Code Playgroud)
仅供参考,正如@Sobrique 在评论中指出的那样,主循环内my @line =...
的整个foreach
循环while (<>)
可以用一行代码替换(perl 有一些非常好的语法糖):
@record{@headers} = map { s/^\s+$//, $_ } split /\t/;
Run Code Online (Sandbox Code Playgroud)
注意:Data::Dump是一个用于漂亮打印整个数据结构的 perl 模块。用于调试,并确保数据结构实际上是您认为的那样。而且,完全不同的是,输出的形式可以复制粘贴到 perl 脚本中并直接分配给变量。
它可用于libdata-dump-perl
软件包中的 debian 和相关发行版。其他发行版可能也打包了它。否则从 CPAN 获取它。或者只是注释掉或删除脚本的最后三行——这里没有必要使用它,它只是打印输出循环中已经打印的数据的另一种方式。
将其另存为,例如,read-tsv.pl
使其可执行chmod +x read-tsv.pl
并运行它:
$ ./read-tsv.pl file.tsv
print @data in a loop:
$data[0]->{SlNo} = 1
$data[0]->{Artist} = A
$data[0]->{VideoTitle} = J
$data[0]->{VideoId} =
$data[0]->{TimeStart} =
$data[0]->{TimeEnd} =
$data[0]->{VideoSpeed} = 1
$data[1]->{SlNo} = 2
$data[1]->{Artist} = B
$data[1]->{VideoTitle} = K
$data[1]->{VideoId} = N
$data[1]->{TimeStart} =
$data[1]->{TimeEnd} =
$data[1]->{VideoSpeed} = 1
$data[2]->{SlNo} = 3
$data[2]->{Artist} = C
$data[2]->{VideoTitle} = L
$data[2]->{VideoId} = O
$data[2]->{TimeStart} = P
$data[2]->{TimeEnd} = Q
$data[2]->{VideoSpeed} = 1
print some individual elements:
1
A
Dump the data:
[
{
Artist => "A",
SlNo => 1,
TimeEnd => "",
TimeStart => "",
VideoId => "",
VideoSpeed => 1,
VideoTitle => "J",
},
{
Artist => "B",
SlNo => 2,
TimeEnd => "",
TimeStart => "",
VideoId => "N",
VideoSpeed => 1,
VideoTitle => "K",
},
{
Artist => "C",
SlNo => 3,
TimeEnd => "Q",
TimeStart => "P",
VideoId => "O",
VideoSpeed => 1,
VideoTitle => "L",
},
]
Run Code Online (Sandbox Code Playgroud)
注意嵌套的 for 循环如何以我们想要的确切顺序打印数据结构(因为我们遍历了@headers
数组),而只是将它与dd
函数一起从Data::Dump
输出中转储到按键名排序的记录(这就是 Data::Dump 处理perl 中的散列没有排序的事实)。
一旦将数据放入这样的数据结构中,就很容易将其插入到 SQL 数据库中,例如mysql / mariadb或postgresql或sqlite3。Perl 具有用于所有这些以及更多的数据库模块(请参阅DBI)。
(在Debian等,这些被打包成libdbd-mysql-perl
,libdbd-mariadb-perl
,libdbd-pg-perl
,libdbd-sqlite3-perl
,和libdbi-perl
其他的发行版都会有不同的包名)
顺便说一句,主解析循环也可以使用另一个名为Text::CSV 的perl 模块来实现,它可以解析 CSV 和类似的文件格式,如 Tab 分隔。或者使用DBD::CSV,它Text::CSV
允许您打开 CSV 或 TSV 文件并对其运行 SQL 查询,就像它是 SQL 数据库一样。
事实上,使用这些模块将 CSV 或 TSV 文件导入 SQL 数据库是一个相当简单的 10-15 行脚本,其中大部分是样板设置的东西……实际算法是一个简单的 while 循环来运行一个对源数据进行 SELECT 查询并将 INSERT 语句插入到目标中。
这两个模块都为 debian 等打包,如libtext-csv-perl
和libdbd-csv-perl
. 可能也打包用于其他发行版。并且一如既往地在 CPAN 上可用。
归档时间: |
|
查看次数: |
393 次 |
最近记录: |