讀取文件並儲存為數組而不跳過空字元串
File.tsv
是一個製表符分隔的文件,有 7 列:cat File.tsv 1 A J 1 2 B K N 1 3 C L O P Q 1
以下讀取
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[@]}
for ((i = 0; i < nA; i = i + 7)); do SlNo="${A[i]}" Artist="${A[$((i + 1))]}" VideoTitle="${A[$((i + 2))]}" VideoId="${A[$((i + 3))]}" TimeStart="${A[$((i + 4))]}" TimeEnd="${A[$((i + 5))]}" VideoSpeed="${A[$((i + 6))]}" done
問題
tsv 文件中的某些條目是空的,但在讀取文件時會跳過空值。
筆記
是 tsv 文件,空值前面和後面都有一個製表符。
所需的解決方案
讀取空值並將其儲存在數組中。
正如我在評論中所說,這不是 shell 腳本的工作。bash(和類似的 shell)用於協調其他程序的執行,而不是用於處理數據。
改用任何其他語言 - awk、perl 和 python 都是不錯的選擇。它將更容易編寫,更容易閱讀和維護,而且速度更快。
這是一個範例,說明如何將文本文件讀入 中的雜湊數組 (AoH)
perl
,然後在各種列印語句中使用數據。AoH 是一種資料結構,正如它的名字所說的那樣 - 一個數組,其中每個元素都是一個關聯數組(又名雜湊)。
順便說一句,這也可以使用數組數組 (AoA) 資料結構(也稱為 List of Lists 或 LoL)來完成,但是能夠通過欄位名稱訪問欄位而不是記住欄位編號會很方便.
您可以在 perl 隨附的 Perl Data Structures Cookbook 中閱讀有關 perl 資料結構的更多資訊。執行
man perldsc
或perldoc perldsc
。perllol
您可能也想閱讀perlreftut
。如果您不perldata
熟悉 perl 變數(“ Perl 具有三種內置數據類型:標量、標量數組和標量關聯數組,稱為雜湊”。“標量”是任何單個值,例如數字或字元串或對另一個變數的引用)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;
僅供參考,正如@Sobrique 在評論中指出的那樣,主循環內
my @line =...
的整個循環可以只用一行程式碼替換(perl 有一些非常好的語法糖):foreach``while (<>)
@record{@headers} = map { s/^\s+$//, $_ } split /\t/;
注意: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", }, ]
注意嵌套的 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 上提供。