;
次の形式(4列、区切り)の大きなテキストファイル(5m行)があります。
文字列1;文字列3;
これ最初の3つの文字列(SHA1)という単一のIDを形成します。アプリケーションID(それでこれを簡単にすることができます:)appId; userId
。 2番目の列(string2または2番目の部分アプリケーションID)自体は、カンマで区切られたいくつかの部分で構成できます,
。ファイルがソートされました。
次のように、各アプリケーションのユーザーリストを取得したいと思います。
入力する文書:
app1, user1
app1, user2
app1, user3
app2, user1
出力文書:
app1: user1, user2, user3
app2: user1
「本物」の一部入力する文書:
44a934ca4052b34e70f9cb03f3399c6f065becd0;bf038823f9633d25034220b9f10b68dd8c16d867;309;8ead5b3e0af5b948a6b09916bd271f18fe2678aa
44a934ca4052b34e70f9cb03f3399c6f065becd0;bf038823f9633d25034220b9f10b68dd8c16d867;309;a21245497cd0520818f8b14d6e405040f2fa8bc0
5c3eb56d91a77d6ee5217009732ff421e378f298;200000000000000001000000200000,6fd299187a5c347fe7eaab516aca72295faac2ad,e25ba62bbd53a72beb39619f309a06386dd381d035de372c85d70176c339d6f4;16;337556fc485cd094684a72ed01536030bdfae5bb
5c3eb56d91a77d6ee5217009732ff421e378f298;200000000000000001000000200000,6fd299187a5c347fe7eaab516aca72295faac2ad,e25ba62bbd53a72beb39619f309a06386dd381d035de372c85d70176c339d6f4;16;382f3aaa9a0347d3af9b35642d09421f9221ef7d
5c3eb56d91a77d6ee5217009732ff421e378f298;200000000000000001000000200000,6fd299187a5c347fe7eaab516aca72295faac2ad,e25ba62bbd53a72beb39619f309a06386dd381d035de372c85d70176c339d6f4;16;396529e08c6f8a98a327ee28c38baaf5e7846d14
「本物」出力ファイルは次のようにする必要があります。
44a934ca4052b34e70f9cb03f3399c6f065becd0;bf038823f9633d25034220b9f10b68dd8c16d867;309:8ead5b3e0af5b948a6b09916bd271f18fe2678aa, a21245497cd0520818f8b14d6e405040f2fa8bc0
5c3eb56d91a77d6ee5217009732ff421e378f298;200000000000000001000000200000,6fd299187a5c347fe7eaab516aca72295faac2ad,e25ba62bbd53a72beb39619f309a06386dd381d035de372c85d70176c339d6f4;16:337556fc485cd094684a72ed01536030bdfae5bb, 382f3aaa9a0347d3af9b35642d09421f9221ef7d, 396529e08c6f8a98a327ee28c38baaf5e7846d14
どうすればいいですか?
編集する:また、各アプリケーションには何千人ものユーザーがいる可能性がありますが、キューはどのくらい長くなりますか?行の長さに制限がありますか?
答え1
パールから
perl -F';' -lane 'push @{$h{join ";",@F[0..2]}},$F[3];
END{
for(sort keys %h){
print "$_: ". join ",",@{$h{$_}};
}
}' your_file
連想配列を使用して同様のことを行うことができなければなりませawk
んが、私はこれに精通していないので、awk
実際のコードに貢献することはできません。
説明する
以下は、「魔法」をできるだけ少なく使用する上記のコードの拡張バージョンです。
open($FH,"<","your_file");
while($line=<$FH>){ # For each line in the file (accomplished by -n)
chomp $line; # Remove the newline at the end (done by -l)
# The ; is set by -F and storing the split in @F done by -a
@F = split /;/,$line # Split the line into fields on ;
$app_id = join ";",@F[0..2]; # AppID is the first 3 fields
push @{$h{$app_id}},$F[3]; # The 4th field is added onto the hash
} # The whole file has been read at this point.
foreach $key (sort keys %h){ # Sort the hash by AppID
print "$key: " . join ",",@{h{$key}}."\n"; # Print the array values
# The newline ("\n") added at the end is also done by -l
}
これで、push
この文だけを詳しく説明できます。
push
通常、配列変数に要素を追加するために使用されます。たとえば、push @a,$x
変数の内容を
$x
配列に追加します@a
。ファイルを1行ずつ読み込むループがハッシュテーブル(
%h
)を埋めています。ハッシュのキーはAppIDであり、各キーに対応する値はそのAppIDに関連付けられているすべてのユーザーIDを含む配列です。これは匿名配列です(名前なし)。 Perlは配列参照として実装されています(Cポインタとやや似ています)。%h
AppID に対応する値は$app_id
で表されるため、Perl$h{$app_id}
配列 sigial(@
) を追加するとハッシュ値を配列として処理し (配列参照逆参照)、現在のユーザー ID をここにプッシュします。「Perlish」のように感じることができるもう1つの選択肢は、4番目のフィールドを現在の値にリンクすることです。
while(...) { ... $h{$app_id} = $h{$app_id} . ",$F[3]" } foreach $key (sort keys %h) { print "$_: $h{$_}" }
Perlは
.
文字列連結演算子です。
説明されたコードでは、perl -e '...'
構文の強調表示がコードに到達し、読みやすくするためにラッパーを省略しました。
答え2
appId
ファイルがソートされたと言われましたが、単純なループを使用して古い文字列のメモリのみを保存することは可能ですか? @Qeoleのアプローチと少し似ていますが、シェルの区切り文字機能と文字列比較を使用してsed
正規表現のオーバーヘッドを回避します。read
#!/bin/bash
appId=""
while IFS=\; read -r s1 s2 s3 userId; do
if [ "$s1;$s2;$s3" == "$appId" ]; then
printf ', %s' "$userId"
else
appId="$s1;$s2;$s3"
printf '\n%s:%s' "$appId" "$userId"
fi
done < yourfile
printf '\n'
注:これにより、出力の先頭に追加の改行が印刷されますが、追加の複雑さを最小限に抑えることでこれを防ぐことができます。大きな打撃を受けなければならないかなりこの種のタスクには高速ですが、そうでない場合は、ほぼすべての同様のスクリプト言語で再実装できます。
答え3
そしてsed
:
sed 's/;/:\t/3;H;1h;x
s/^\(\([^:]*\):.*\)\n\2/\1/
/\n/P;//g;h;$!d' <input |
tr : \\n
印刷:
44a934ca4052b34e70f9cb03f3399c6f065becd0;bf038823f9633d25034220b9f10b68dd8c16d867;309
8ead5b3e0af5b948a6b09916bd271f18fe2678aa
a21245497cd0520818f8b14d6e405040f2fa8bc0
5c3eb56d91a77d6ee5217009732ff421e378f298;200000000000000001000000200000,6fd299187a5c347fe7eaab516aca72295faac2ad,e25ba62bbd53a72beb39619f309a06386dd381d035de372c85d70176c339d6f4;16
337556fc485cd094684a72ed01536030bdfae5bb
382f3aaa9a0347d3af9b35642d09421f9221ef7d
396529e08c6f8a98a327ee28c38baaf5e7846d14
tr
グループを同じ行に保持するために削除できます。これID:
この場合、コロンで区切られます。\t
最初の行のエスケープ文字をリテラル文字に置き換える必要があるかもしれません<tab>
。あるいは、Abを完全に取り除くこともできます\t
。これにより、出力が読みやすくなります。(私の考えでは)正規表現の機能には重要ではありません。
答え4
1つのsed
答え:
sed ': l;N;s/^\([^;]\+;[^;]\+;[^;:]\+\)[;:] *\(.*\)\n\1;\(.*\)/\1: \2, \3/;tl;P;D' input_file.txt
ファイルは一度だけ読んでパフォーマンスが大幅に悪くはありませんが、詳細についてはお話できません。
詳細:
sed ': l; # Label l
N; # Add next line of input to pattern space
s/^\([^;]\+;[^;]\+;[^;:]\+\)[;:] *\(.*\)\n\1;\(.*\)/\1: \2, \3/;
# If two lines in pattern space start with same AppID, then
# take user ID and append it to first line, then delete second line
tl; # If previous substitution succeeded, i.e. we scanned two lines with
# same AppID, then loop to label l. Else go on…
P; # Print first line from pattern space (here there should be two lines
# in pattern space, starting with a different AppID)
D; # Delete first line of pattern space; start script again with
# remaining text in pattern space, or next input line if pattern
# space is empty
' input_file.txt
(しかし、行の長さの潜在的な制限についてはわかりません。申し訳ありません。)