スペースを含むテキスト列を1つのフィールドとして扱う

スペースを含むテキスト列を1つのフィールドとして扱う

次の形式のファイルがあります。
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 は大容量データファイルでかなり遅くなることがありますが、あなたの場合は試してみることができます。

関連情報