首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >提取和格式化高可变文本文件的Awk

提取和格式化高可变文本文件的Awk
EN

Stack Overflow用户
提问于 2015-11-22 23:06:46
回答 2查看 152关注 0票数 0

我在处理的文本文件就是一团糟。这是我要买的一辆二手房车的服务记录,也是正则情人的噩梦

它既有不一致的字段分隔符,也有不一致的字段数,其中的行是两种类型之一:

代码语言:javascript
复制
Type 1 (11 columns):
UNIT   Mile  GnHr  R.O. Ln  Service  Description                Mechanic   Hours  $ Amt
7-9918;57878 1698 01633 021;0502-00C ENG OIL/ FILTERT IF NEEDED;M02 JOSE A. SANCHEZ;0.80;80.00

Type 2 (10 columns)
UNIT   Mile  GnHr  R.O. Ln  Service  Description   Hours  $ Amt
7-9918;55007 1641 [9564 007;ELE-BAT-BAT-0-0AAA;BATTERY AAA ALL BRANDS;2;31.12

我把所有的标题都去掉了,但把它们放回这里,仅供参考。在类型2行中,缺少机械场。

我用分号替换了多个空格,所以我现在有一个文件,其中一些行有10个字段,一些行有11个字段,有时字段分隔符是一个空格,在其他情况下它是分号,有些字段有合法的嵌入空格(Description and Mechanic)。

我试着用awk找到一种方法:

1)提取每个字段,并能够用统一的OFS打印出来(分号为首选)

2)如果缺少机械场,请插入该字段并打印N/A或-用于该机械师

我可以自己处理列标题和内容,只是无法破解如何处理这个文件中FS问题和可变列数的代码。我可以用grep列出我需要的具体信息,但是如果能把它导入到电子表格或DB中,我会非常高兴。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-11-23 04:29:08

你的输入文件还不错。假设输入文件是分号分隔的:

  1. $2中的所有空白字符替换为;,以便将其分割为单独的字段以供输出。
  2. 如果$3中有空白,那么将第一个空白替换为; (因为它同时包含服务和描述,所以您需要将它们分开),否则
  3. 这是一种没有机械师指定的行格式,所以在$4之后添加空的机械文本(描述)

然后打印这条线:

代码语言:javascript
复制
$ awk 'BEGIN{FS=OFS=";"} {gsub(/ /,OFS,$2)} !sub(/ /,OFS,$3){$4=$4 OFS "N/A"} 1' file
7-9918;57878;1698;01633;021;0502-00C;ENG OIL/ FILTERT IF NEEDED;M02 JOSE A. SANCHEZ;0.80;80.00
7-9918;55007;1641;[9564;007;ELE-BAT-BAT-0-0AAA;BATTERY AAA ALL BRANDS;N/A;2;31.12

如果您想对各个字段做任何事情:

代码语言:javascript
复制
$ cat tst.awk
BEGIN { FS=OFS=";" }
{ gsub(/ /,OFS,$2) }
!sub(/ /,OFS,$3) { $4 = $4 OFS "N/A" }
{
    $0 = $0
    print
    for (i=1; i<=NF; i++) {
        print NR, i, $i
    }
    print ""
}

代码语言:javascript
复制
$ awk -f tst.awk file
7-9918;57878;1698;01633;021;0502-00C;ENG OIL/ FILTERT IF NEEDED;M02 JOSE A. SANCHEZ;0.80;80.00
1;1;7-9918
1;2;57878
1;3;1698
1;4;01633
1;5;021
1;6;0502-00C
1;7;ENG OIL/ FILTERT IF NEEDED
1;8;M02 JOSE A. SANCHEZ
1;9;0.80
1;10;80.00

7-9918;55007;1641;[9564;007;ELE-BAT-BAT-0-0AAA;BATTERY AAA ALL BRANDS;N/A;2;31.12
2;1;7-9918
2;2;55007
2;3;1641
2;4;[9564
2;5;007
2;6;ELE-BAT-BAT-0-0AAA
2;7;BATTERY AAA ALL BRANDS
2;8;N/A
2;9;2
2;10;31.12
票数 1
EN

Stack Overflow用户

发布于 2015-11-25 01:17:46

我的一个朋友也给我发了这个解决方案,是用perl完成的:

代码语言:javascript
复制
#!/usr/bin/env perl -w

use strict;
use warnings;

#                                                                                                     1         1         1         1         1
#           1         2         3         4         5         6         7         8         9         0         1         2         3         4
# 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
# Type 1:
# 7-9918  55007 1641 [9564 022            0211     INTERIOR MISC.                  M02 JOSE A. SANCHEZ                0.00       0.00
# Type 2:
# 7-9918  57878 1698 01633 001            FUE-LPG-LPG-S-GAS      PROPANE GAS BULK PURCHASE                             5        24.00

my $delim="\t";

while (<STDIN>) {
    #print $_;

    # Both formats are the same at this point
    print substr($_, 0, 6) . $delim;
    print substr($_, 8, 5) . $delim;
    print substr($_, 14, 4) . $delim;
    print substr($_, 19, 5) . $delim;
    print substr($_, 25, 3) . $delim;

    my $qty = substr($_, 109, 11);
    $qty =~ s/^\s*//g;
    $qty =~ s/\s*$//g;

    if ($qty =~ /^\d+\.\d{2}$/) {
        # Type 1
        print substr($_, 40, 9) . $delim;
        print substr($_, 49, 32) . $delim;
        # print substr($_, 81, 32) . $delim; # Technician name
        print $qty . $delim;
    } elsif ($qty =~ /^[-]?\d+$/) {
        # Type 2
        print substr($_, 40, 23) . $delim;
        print substr($_, 63, 46) . $delim;
        print $qty . $delim;
    }
    print sprintf("%.2f", substr($_, 120, 11)) . "\n";
}

1;
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33861350

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档