awk - 错误的 awk : translate spaces to tabs for one field only

标签 awk

我收到了表格数据的 PDF 文件,我已将其转换为纯文本进行处理。

pdftotext -nopgbrk -layout file.pdf

这做得相当不错,但使用空格来分隔/分隔列中的字段,并且似乎主要对保留视觉布局而不是“结构”布局感兴趣,即,没有一致或可靠的分隔符。所以现在我将 2 个或更多空格转换为制表符:

sed -i 's/[[:space:]]\{2,\}/\t/g' file.txt

使用 cat -vte 我发现这在文件中放置制表符做得非常好......但是,我想问的第二个字段有一些不一致之处你的帮助。

请看下面的比较说明:

正常/预期结果:

79879   5.6     0.5     MG      EN      SQ      TFK World Report 09-24-2004     Time for Kids Editors,  ORD1915643
79880   5.5     0.5     MG      EN      SQ      TFK World Report 10-01-2004     Time for Kids Editors,  ORD1915643
79881   6.0     0.5     MG      EN      SQ      TFK World Report 10-08-2004     Time for Kids Editors,  ORD1915643
79882   5.5     0.5     MG      EN      SQ      TFK World Report 10-22-2004     Time for Kids Editors,  ORD1915643
79883   5.9     0.5     MG      EN      SQ      TFK World Report 10-29-2004     Time for Kids Editors,  ORD1915643

Some oddities and inconsistencies:

72      5.2 3.0 MG      EN      LS      Ramona and Her Father   Cleary, Beverly ORD2111460
491     4.8 4.0 MG      EN      LS      Ramona and Her Mother   Cleary, Beverly ORD1748201
134     5.6 3.0 MG      EN      LS      Ramona Quimby, Age 8    Cleary, Beverly ORD1748201
29      4.7     5.0 MG  EN      LS      From the Mixed-Up Files of Mrs. Basil E.        Konigsburg, E.L.        ORD1525579

Note that the 'smushing' effect may occur in either field 2 or field 3 ...AND, that the number of fields differs with the 'normal' results by either 1 or 2.

...So, to solve this I've tried stuff like the following:

awk -F'\t' 'OFS="\t";$1 ~ /^[[:digit:]]/{print $1,gensub(/[[:space:]]/,"\t","g",$2),$3,$4,$5,$6,$7}' file.txt

这似乎将每行或至少大多数行加倍并切断字段。

编辑 这似乎有效……到目前为止,仍在测试中。

awk -F'\t' '{$2 = gensub( /[[:space:]]/, "\t", "g", $2 );
             $3 = gensub( /[[:space:]]/, "\t", "g", $3 )}
             {OFS="\t";print}' file.txt

有没有使用 awk 解决这个问题的简单方法?

更新

有些人要求提供一个代表我的空格键转换之前的状态的样本。以下表示文档中上一个示例所在位置附近的示例。看起来差不多……除了一个 [下方] 是间隔的,另一个 [上方] 是标签式的。请注意 pdftotext 处理下面不同示例中的第 2 列的方式...有时拆分,有时单独列。

示例 1:

    72   5.2 3.0 MG       EN   RP     Ramona and Her Father                     Cleary, Beverly              ORD0630871
are orphans
   491   4.8 4.0 MG       EN   RP     Ramona and Her Mother                     Cleary, Beverly              ORD0785414
are also orphans
   186   4.8 4.0 MG       EN   RP     Ramona Forever                            Cleary, Beverly              ORD0630871
forever the orphan

Sample 2:

  79871    5.7   0.5   MG   EN    SQ        TFK World Report 03-18-2005         Time for Kids Editors,       ORD1915643
  79872    5.8   0.5   MG   EN    SQ        TFK World Report 04-01-2005         Time for Kids Editors,       ORD1915643
  79873    6.0   0.5   MG   EN    SQ        TFK World Report 04-08-2005         Time for Kids Editors,       ORD1915643

UPDATE 2

Made the following changes to Ed's submission. Thinking it could be simplified, but it works. It has to allow for the orphaned lines.

$1 ~ /^[[:digit:]]+/{
   for (i=1;i<=6;i++)
      printf "%s\t", $i

   n = split($0,tmp,/  +/)

   for (i=2;i>=0;i--)
      printf "%s\t", tmp[n-i]

   print ""
}
$1 ~ /^[^[:digit:]]+/ {print $0}

也许这样更漂亮:

{
        if ($1 ~ /^[[:digit:]]+/) {
                for (i=1;i<=6;i++)
                printf "%s\t", $i

                n = split($0,tmp,/  +/)

                for (i=2;i>=0;i--)
                printf "%s\t", tmp[n-i]

                print ""
        }
        else print $0;
}

最佳答案

您原来的 awk 脚本似乎将每一行加倍,因为 OFS="\t" 的计算结果为 true,因此打印当前行。将其放在 BEGIN{} block 中以避免重复:

gawk -F'\t' 'BEGIN{OFS=FS} $1 ~ /^[[:digit:]]/ {print $1,gensub(/[[:space:]]/,"\t","g",$2),$3,$4,$5,$6,$7}' file.txt

请注意 gensub()gawk 的一部分,因此不可移植。你可以用这个便携地实现同样的事情:

awk -F'\t' 'BEGIN{OFS=FS} $1 ~ /^[[:digit:]]/ {gsub(/[[:space:]]/,"\t",$2); print $1,$2,$3,$4,$5,$6,$7}' file.txt

就是说……通过您的更新,我可以看到原始数据的格式足够好,我们可以按原样处理它。令人恼火的是,第 2 列和第 4 列之间只有一个空格,或者我们可以简单地使用多个空格作为字段分隔符。但它仍然是一种可预测的输入格式。

似乎对于前 6 个字段,输入由“任何空格”分隔,而对于后 3 个字段,输入由“两个或更多空格”分隔。考虑到这一点,我们可以使用以下 awk 来解析您的输入数据:

#!/usr/bin/awk -f

BEGIN {
  FS="  +";
  fmt="----\n1=%s\n2=%s\n3=%s\n4=%s\n5=%s\n6=%s\n7=%s\n8=%s\n9=%s\n";
}

{
  # Grab the right-hand fields, separated by FS
  a[7]=$(NF-2); a[8]=$(NF-1); a[9]=$NF;

  # Then trim the line and grab initial fields, separated by whitespace
  sub(/^ +/, "");
  split($0, easy, /[[:space:]]+/);
  for(i=1;i<=6;i++) {
    a[i]=easy[i+1];
  }

  printf(fmt, a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9]);
}

这假设您的倒数第二个字段和最后一个字段之间的间隔总是超过 1 个空格(如您在问题中提供的输入数据所示)。如果这不是一个安全的假设,您/我们可以围绕此进行编码。

根据需要调整输出。

关于awk - 错误的 awk : translate spaces to tabs for one field only,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13150072/

相关文章:

linux - 如何使用 tail 和 awk 从文件中获取数据

bash - 文件操作循环

for-loop - 使用 awk 每隔一个字段打印一次

linux - shell脚本linux减去参数grep

regex - 如何用awk拆分单列的输出?

Bash/Shell - 带空格的路径搞砸了

bash - 在 awk 中设置 bash 变量

python - 唯一计算相邻两行的百分比差异

Linux Bash 脚本

bash - awk 比较两个文件 - 从第一个文件的条件中删除第二个文件的行