次の形式のファイルがあります。
INTEGER INTEGER TEXT
テキストはUnicodeであり、スペースを含めることができます。
printfを使用してファイルの最初のINTEGERとTEXTを特定の形式で印刷するためにawkを使用しようとしています。
問題:一部の行のTEXTにスペースがあるため、$ 3には完全なTEXTがないため、より多くのフィールドで行が破損します。
例:
12 42956 Cinema - 3D/Multiplex
7 12560 Status Update
5 184 Movie
私のアプローチは次のとおりです。
awk '{ c=$3; for(i=4; i< NF;++i){c=c" "$i}; printf "<tag>%d</tag>\n<tag>%s</tag>\n", $1,c}';
しかし、もっと良い方法があると思います。
答え1
awk
データが明確に指定されたレコードから来た場合に便利です。このデータは使用できません。ただし、データは「integer stuff the_rest
フォーマット」で、「integer
」または「」stuff
にスペースはありません。これがまさにread
ユーティリティが読みたいものです。読み取るように指定した変数と同じスペースで区切られた単語を読み、行の「残り」を最後の変数に入れます。
bash-4.4$ while read -r integer stuff the_rest; do printf '%d\t"%s"\n' "$integer" "$the_rest"; done <data
12 "Cinema - 3D/Multiplex"
7 "Status Update"
5 "Movie"
すべての末尾のスペースを自動的に削除します。
答え2
スキーマに基づいてフィールドを抽出する方が次の方がperl
良い場合がよくありますawk
。
perl -lne '
if (/^\s*(\d+)\s*\S+\s*(.*?)\s*$/) {
print "<tag>$1</tag><tag>$2</tag>"
}'
あなたのコメントは以下を提供します。
<tag>12</tag><tag>Cinema - 3D/Multiplex</tag>
<tag>7</tag><tag>Status Update</tag>
<tag>5</tag><tag>Movie</tag>
これは、必要に応じて適切なHTMLエンコードなどの高度な操作を実行できることを意味します。たとえば、次のようになります。
perl -Mopen=locale -MHTML::Entities -lne '
if (/^\s*(\d+)\s*\S+\s*(.*?)\s*$/) {
print map {"<tag>" . encode_entities($_) . "</tag>"} $1, $2
}'
またはXMLエンコーディング:
perl -Mopen=locale -MXML::LibXML -lne '
if (/^\s*(\d+)\s*\S+\s*(.*?)\s*$/) {
print map {
my $e = XML::LibXML::Element->new("tag");
$e->appendText($_);
$e->toString} $1, $2
}'
答え3
$ 2(とにかく未使用)を未使用文字(文字列に存在しない文字)に置き換えます。その後、次の操作を行います。
awk '{$2="+";print}' input-file.txt | awk -F "+" '{printf "<tag>%d</tag>\n<tag>%s</tag>\n",$1,$2}'
上記では、プラス記号「+」を区切り記号として使用した。
最もエレガントな解決策ではありませんが、簡単です。
答え4
これは大きなファイルではなくテキストが常に最後にあるので、代わりに次のような古典的なbashアプローチを使用することを検討することができます。
while IFS=' ' read -r int1 int2 text;do
#do your stuff
done <file
while - readの場合と同様に、readコマンドの最後のvar $ textは残りのすべてのフィールドを1つのフィールドとして取得します。
テスト:
$ IFS=' ' read -r int1 int2 text <<<"10 5 some text here"
$ echo "$text"
some text here
読み込み中、Bash は大容量データファイルでかなり遅くなることがありますが、あなたの場合は試してみることができます。