错误的awk:仅将一个字段的空格转换为制表符

错误的awk:仅将一个字段的空格转换为制表符,awk,Awk,我收到了一个表格数据的PDF文件,我已将其转换为纯文本进行处理 pdftotext -nopgbrk -layout file.pdf 这是一项相当不错的工作,但使用空格分隔/分隔列中的字段,并且似乎主要关心的是保留视觉布局而不是“结构”布局,即没有一致或可靠的分隔符。现在我将2个或更多的空格转换为制表符: sed -i 's/[[:space:]]\{2,\}/\t/g' file.txt 使用cat-vte我发现这在文件中放置选项卡方面做得非常好……但是,我想请您帮助的第二个字段有一些不

我收到了一个表格数据的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 这似乎是每行或至少大多数行的两倍,并切断字段

编辑 这似乎是工作…到目前为止,仍在测试

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 72 5.2 3.0 MG EN RP Ramona和她的父亲Cleary Beverly ORD0630871 你是孤儿吗 491 4.8 4.0 MG EN RP Ramona和她的母亲Cleary Beverly ORD0785414 他们也是孤儿 186 4.8 4.0 MG EN RP雷蒙纳永久克利里,贝弗利ORD0630871 永远的孤儿 样本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 79871 5.7 0.5 MG EN SQ TFK《世界报告》03-18-2005《儿童时代》编辑,ORD1915643 79872 5.8 0.5 MG EN SQ TFK《世界报告》04-01-2005《儿童时代》编辑,ORD1915643 79873 6.0 0 0.5 MG EN SQ TFK《世界报告》04-08-2005《儿童时代》编辑,ORD1915643 更新2

对教育署的意见书作出以下更改。我认为它可以简化,但它是有效的。它必须考虑到孤立线

$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}
$1~/^[:位:]+/{
对于(i=1;i=0;i--)
printf“%s\t”,tmp[n-i]
打印“”
}
$1~/^[^[:数字:]+/{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;
}
{
如果($1~/^[:位:]+/){
对于(i=1;i=0;i--)
printf“%s\t”,tmp[n-i]
打印“”
}
否则打印$0;
}
尝试执行以下操作:

column -t file.txt > newfile.txt

我想只有从末尾算起的第二列和第三列中才能有空格

我会尝试一些类似Python的东西:

import re
import sys

for line in sys.stdin:
    start = line.rstrip().split(None, 6)
    end = start.pop().rsplit(None, 1)
    mid = re.split('\s\s+', end.pop(0), maxsplit=1)
    print '\t'.join(start + mid + end)

编辑:好的,如果您想继续使用coreutils/textutils工具,下面是一个sed脚本,它的功能与上面的Python大致相同:

#!/bin/sed -f
s/^ *//
s/ \+/\t/
s/ \+/\t/
s/ \+/\t/
s/ \+/\t/
s/ \+/\t/
s/ \+/\t/
s/ \+\([^ ]\+\) *$/\t\1/
s/  \+/\t/
或者,作为一个班轮:

sed -e 's/^ *//; s/ \+/\t/; s/ \+/\t/; s/ \+/\t/; s/ \+/\t/; s/ \+/\t/; s/ \+/\t/; s/ \+\([^ ]\+\) *$/\t\1/; s/  \+/\t/'

请尝试以下操作,而不是
{print$1,gensub(/[:space:]/,“\t”,“g”,“$2),$3,$4,$5,$6,$7}

{ $2 = gensub( /[[:space:]]/, "\t", "g", $2 ); print }

与其让我们从一个sed命令的输出开始(这可能是损坏您的数据的原因),不如在您对其运行该sed命令之前发布您的数据,让我们从那里开始。我怀疑,既然你说PDF转换工具保留了“可视化布局”,那么正确的解决方案可能只是简单地使用gawk的FIELDWIDTHS功能,这样你就可以根据字段的宽度解析PDF转换器的输出,而不是试图计算出表示字段分隔符需要多少空格

编辑:这里有一个基于match()的比较解决方案,但我现在认为@ghoti是正确的,而且解决方案比这个简单:

$ cat file
    72   5.2 3.0 MG       EN   RP     Ramona and Her Father     Cleary, Beverly    ORD0630871
   491   4.8 4.0 MG       EN   RP     Ramona and Her Mother     Cleary, Beverly    ORD0785414
  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
$
$ cat tst.awk
BEGIN {
   whl = "([[:digit:]]+)"
   dec = "([[:digit:]]+[.][[:digit:]]+)"
   wrd = "([^ ]+)"
   rst = "(.*)"
   s   = "[ ]+"
   fmt = whl s dec s dec s wrd s wrd s wrd s rst
}
{
   match($0,fmt,arr)
   split(arr[7],tmp,/  +/)
   arr[7] = tmp[1]
   arr[8] = tmp[2]
   arr[9] = tmp[3]

   for (i=1;i<=9;i++)
      printf "<%s>", arr[i]
   print ""
}
$
$ awk -f tst.awk file
<72><5.2><3.0><MG><EN><RP><Ramona and Her Father><Cleary, Beverly><ORD0630871>
<491><4.8><4.0><MG><EN><RP><Ramona and Her Mother><Cleary, Beverly><ORD0785414>
<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>
$cat文件
72 5.2 3.0 MG EN RP Ramona和她的父亲Cleary Beverly ORD0630871
491 4.8 4.0 MG EN RP Ramona和她的母亲Cleary Beverly ORD0785414
79872 5.8 0.5 MG EN SQ TFK《世界报告》04-01-2005《儿童时代》编辑,ORD1915643
79873 6.0 0 0.5 MG EN SQ TFK《世界报告》04-08-2005《儿童时代》编辑,ORD1915643
$
$cat tst.awk
开始{
whl=“([:位:][]+)”
dec=“([:digit:]+[.][[:digit:]+])”
wrd=“([^]+)”
rst=“(*)”
s=“[]”
fmt=whl s dec s dec s wrd s wrd s wrd s rst
}
{
匹配($0,fmt,arr)
拆分(arr[7],tmp,/+/)
arr[7]=tmp[1]
arr[8]=tmp[2]
arr[9]=tmp[3]

对于(i=1;i您的原始awk脚本似乎每行加倍,因为
OFS=“\t”
的计算结果为
true
,因此打印当前行。将其放入
BEGIN{}
块以避免重复:

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]);
}
!/usr/bin/awk-f
开始{
FS=“+”;
fmt=“-----\n1=%s\n2=%s\n3=%s\n4=%s\n5=%s\n6=%s\n7=%s\n8=%s\n9=%s\n”;
}
{
#抓取右侧字段,用FS分隔
a[7]=$(NF-2);a[8]=$(NF-1);a[9]=$NF;
#然后修剪行并获取初始字段,用空格分隔
sub(/^+/,“”);
拆分($0,简单,/[:space:]+/);

对于(i=1;它拆分了标题和作者等字段。太过激进——也不一致。不过,谢谢。当我指定分隔符时,它的作用与pdftotext差不多,但添加了其他“oddities”。
column-t-s'\t
gawk -F'\t' 'BEGIN{OFS=FS} $1 ~ /^[[:digit:]]/ {print $1,gensub(/[[:space:]]/,"\t","g",$2),$3,$4,$5,$6,$7}' file.txt
awk -F'\t' 'BEGIN{OFS=FS} $1 ~ /^[[:digit:]]/ {gsub(/[[:space:]]/,"\t",$2); print $1,$2,$3,$4,$5,$6,$7}' file.txt
#!/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]);
}