【问题标题】:Using awk substr on fixed width file在固定宽度文件上使用 awk substr
【发布时间】:2020-02-27 17:34:40
【问题描述】:

我有一个固定宽度的文件,我想按前四个字符定义的值将行拆分为名为@9​​87654321@ 和file_1986.dat 的文件。拿这个最小的文件:

$ cat foo.dat
1985tiny dancer
1986largechicken
1985hey  jude

我想这样结束:

$ cat file_1985.dat
1985tiny dancer
1985hey  jude

还有这个:

$ cat file_1986.dat
1986largechicken

我很确定我需要这样做:

awk -F, '{if(???)print > "file_1985.dat";else print > "file_1986.dat"}' foo.dat

其中??? 涉及到substr 的一些使用。有人可以在这里建议吗?

【问题讨论】:

    标签: awk substr


    【解决方案1】:

    使用substr($0, 1, 4) 获取您想在文件名中添加的4 个字符(从字符1 开始,长度为4 的子字符串):

    awk '{ 
      out = "file_" substr($0, 1, 4) ".dat" # set filename
      if (out != prev) close(prev)          # close previous file
      print >> out                          # write to file
      prev = out                            # remember filename to check on next line
    }' foo.dat
    

    文件名out 来自将字符串文字与substr 的结果相连接的结果。此变量用于确定print 的输出最终进入的文件。

    >>以“追加模式”打开一个文件,这意味着如果你重新打开同一个文件,你不会丢失之前的内容。

    测试一下:

    $ awk '{ out = "file_" substr($0, 1, 4) ".dat"; if (out != prev) close(prev); print >> out; prev = out  }' foo.dat
    $ cat file_1985.dat 
    1985tiny dancer
    1985hey  jude
    $ cat file_1986.dat 
    1986largechicken
    

    close 的使用是防止打开太多文件的预防措施,但如果您的输入不是太大,那么您可以简化为:

    awk '{ print > ("file_" substr($0, 1, 4) ".dat") }' foo.dat
    

    关于性能,您可以尝试对输入进行排序,以避免重复打开和关闭相同的文件(尽管排序本身需要时间):

    sort -s -k1.1,1.4 foo.dat | awk '{ out = "file_" substr($0, 1, 4) ".dat"; if (out != prev) close(prev); print > out; prev = out  }'
    

    这里我还将>> 更改为>,因为awk 只会打开每个文件一次。

    您还可以通过缓存结果来避免每行重复一次相同的字符串连接:

    {
      ss = substr($0, 1, 4)
      if (!(ss in outs)) {
        outs[ss] = "file_" ss ".dat"
      }
      out = outs[ss]
      if (out != prev) close(prev)
      print >> out
      prev = out
    }
    

    将它放在像script.awk 这样的脚本中,然后像awk -f script.awk foo.dat 这样运行。

    【讨论】:

    • 你能解释一下它是如何知道 1985 年和 1986 年的吗?它是否只在前四个字符中采用任何唯一值?
    • 对于大文件(4Gb)来说,这最终是一个相当缓慢的解决方案。我最初并没有要求速度,但您对如何加快速度有什么建议吗?
    • @boshek 我添加了一些关于如何加快脚本速度的想法。
    【解决方案2】:
    gawk -v FIELDWIDTHS="4 200" '{ print $2 > "file_" $1 ".dat" }' foo.dat
    

    来自手册页: 如果 FIELDWIDTHS 变量设置为以空格分隔的数字列表,则每个字段都应具有固定宽度,并且 gawk 使用指定的宽度拆分记录。每个字段宽度可以 可选地前面有一个以冒号分隔的值,指定在字段开始之前要跳过的字符数。 FS 的值被忽略。为 FS 或 FPAT 分配新值会覆盖 使用 FIELDWIDTHS。

    【讨论】:

    • 在最近的 gawk 版本中,您可以使用 * 作为 FIELDWIDTHS 中的最后一个字符串来代表“剩下的”,而不必指定一个您希望足够大的数字,例如FIELDWIDTHS='4 *'。见lists.gnu.org/archive/html/bug-gawk/2017-05/msg00018.html
    • 我正在使用 gawk 4.2.1 进行测试,但在手册页上没有注意到...?
    【解决方案3】:

    如果您使用的是 GNU awk,或者您的输出文件少于 12 个,那么:

    awk '{print > ("file_"substr($0,1,4)".dat")}' foo.dat
    

    否则为避免“打开的文件过多”错误:

    awk '{out="file_"substr($0,1,4)".dat"; print >> out; close(out)}' foo.dat
    

    【讨论】:

      猜你喜欢
      • 2011-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-22
      • 1970-01-01
      • 2010-11-26
      • 1970-01-01
      相关资源
      最近更新 更多