将包含空格的文本的列视为 1 个字段

将包含空格的文本的列视为 1 个字段

我有一个具有以下格式的文件:
INTEGER INTEGER TEXT

文本为 unicode,可以包含空格。
我正在尝试使用 awk 来使用 printf 以特定格式打印文件中的第一个 INTEGER 和 TEXT 。
问题:由于某些行中的 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 会将所有剩余字段作为一个字段获取。

测试:

$ IFS=' ' read -r int1 int2 text <<<"10 5 some text here"
$ echo "$text"
some text here

Bash while read 在大数据文件中执行速度可能相当慢,但您可以尝试一下您的情况。

相关内容