ファイルを読み取り、配列として保存します。空の文字列をスキップしないでください。

ファイルを読み取り、配列として保存します。空の文字列をスキップしないでください。

File.tsv7つの列があるタブ区切りファイルです。

cat File.tsv
1   A   J               1
2   B   K   N           1
3   C   L   O   P   Q   1

以下は、File.tsv7列のタブで区切られたファイルを読み取り、項目を配列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ファイルの一部のエントリは空ですが、ファイルを読み取るとnull値をスキップします。

ノート

NULL値の前後にタブ文字があるtsvファイル。

必要なソリューション

null 値を読み取り、配列に保存します。

ベストアンサー1

コメントで述べたように、これはシェルスクリプトの仕事ではありません。 bash(および同様のシェル)は、データを処理するのではなく、他のプログラムの実行を調整するために使用されます。

使用どの他の言語 - awk、Perl、Pythonはすべて良い選択です。作成しやすく、読みやすく保つのは簡単です。たくさん急いで。

以下は、テキストファイルをAoH(ハッシュ配列)に読み込みperl、そのデータをさまざまな印刷ステートメントで使用する方法の例です。

AoHは、名前が示すように、各要素が連想配列(ハッシュとも呼ばれる)の配列であるデータ構造です。

これは、AoA(Array of Arrays)データ構造(List of ListsまたはLoLとも呼ばれます)を使用して行うことができますが、フィールド番号を覚えておらずに名前でフィールドにアクセスするのが便利です。

Perl に付属の Perl データ構造 料理書から Perl データ構造の詳細を読むことができます。実行man perldscまたはperldoc perldsc.Perl変数( "perllolperlreftutperldataPerlには、スカラー、スカラー配列、およびスカラー連想配列(ハッシュと呼ばれる)の3つの組み込みデータ型があります。"."スカラー"は、数値、文字列、または数値などの単一の値です。引用する他の変数として)

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 =...とループ全体を1行のコードに置き換えることができます。foreachwhile (<>)非常に良いフレーズ砂糖):

  @record{@headers} = map { s/^\s+$//, $_ } split /\t/;

メモ:データ::ダンプ全体のデータ構造をきれいに印刷するためのPerlモジュールです。デバッグし、データ構造が実際に考えたものと同じであることを確認するのに役立ちます。そして偶然にも、出力はPerlスクリプトにコピーして貼り付け、変数に直接割り当てることができる形式です。

libdata-dump-perlパッケージのDebianおよび関連ディストリビューションで使用できます。他のディストリビューションでもそれをパッケージ化できます。それ以外の場合はCPANから取得します。または、スクリプトの最後の3行をコメントアウトまたは削除してください。ここでは使用する必要はありません。これは、出力ループに印刷されたデータを印刷する別の方法です。

たとえば、別の名前で保存し、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でソートされたレコードをダンプします(これはPerlのハッシュがソートされていないことをData :: Dumpが処理する方法です)。


その他の提案

このようなデータ構造にデータがある場合は、それをSQLデータベースに簡単に挿入できます。mysql/マリアデータベースまたはPostgreSQLまたはSQLite3。 Perlにはデータベースモジュールがあります(参照:データベースインタフェース)これらすべて以上のため。

(Debianなどではlibdbd-mysql-perl、、、、、でパッケージされていますlibdbd-mariadb-perl。他のディストリビューションでlibdbd-pg-perlはパッケージ名が異なります)libdbd-sqlite3-perllibdbi-perl

ところで、メイン構文解析ループは、次のような他のPerlモジュールを使用して実装することもできます。テキスト::CSV、CSVなどのファイル形式(タブ区切りなど)を解析できます。またはDBD::CSVText::CSVCSVファイルまたはTSVファイルを開き、SQLクエリを実行できます。まるでSQLデータベースのように

実際、これらのモジュールを使用してCSVファイルまたはTSVファイルをSQLデータベースにインポートするのは非常に単純な10〜15行のスクリプトであり、ほとんどは定型句設定項目です。実際のアルゴリズムは、SELECTを実行する単純なwhileループです。ソースデータを照会し、INSERTステートメントをターゲットデータに挿入します。

両方のモジュールと同じ Debian 用にパッケージされていlibtext-csv-perlますlibdbd-csv-perl。他のディストリビューション用にパッケージ化することもできます。そしていつものように、CPANで使用することができます。

おすすめ記事