【问题标题】:Data Cleanup with Regex使用正则表达式进行数据清理
【发布时间】:2016-07-17 10:10:48
【问题描述】:

我有很多非常大的文件。

每个文件都包含这样的行:

uuid1 (tab) data1 (vtab) data2 ...  dataN
uuid2 (tab) data1' (vtab) data2' (vtab) data3' (vtab) ...  dataN'
....

每行的 N 都不同。结果需要如下所示:

uuid1 (tab) data1
uuid1 (tab) data2
....
uuid1 (tab) dataN
uuid2 (tab) data1'
uuid2 (tab) data2'
uuid2 (tab) data3'
...  
uuid2 (tab) dataN'
....

我有一个可以完成这项工作的正则表达式,替换:

^([abcdef0123456789]{8}-[abcdef0123456789]{4}-[abcdef0123456789]{4}-[abcdef0123456789]{4}-[abcdef0123456789]{12})\t(.+?)\x0B

与:

\1\t\2\n\1\t

但它很慢,显然需要重复应用。

是否有更快的编程方式在所有文件中执行此操作?

工具箱中可用的工具:unix 工具(sed、awk 等)、python,可能还有 perl。

不求宗教战争,只求务实。

其他信息

这是我使用的完整脚本,基于 Kristof 的脚本,用于处理外循环:

#!/usr/bin/python

import os
import uuid

def processFile( in_filename ):

  out_filename = os.path.splitext(in_filename)[0] + '.result.txt'

  with open(in_filename) as f_in:
    with open(out_filename, 'w') as f_out:
      for line in f_in:
        try:
          # Retrieve the line and split into UUID and data
          line_uuid, data = line.split('\t')
          # Validate UUID
          uuid.UUID(line_uuid)
        except ValueError:
          # Ignore this line
          continue
        # Write each individual piece of data to a separate line
        for data_part in data.rstrip().split('\x0b'):
          f_out.write(line_uuid + '\t' + data_part  + '\n')

for i in os.listdir(os.getcwd()):
  if i.endswith(".txt"): 
    print i
    processFile( i )
    continue
  else:
    continue

【问题讨论】:

    标签: python regex awk sed data-cleaning


    【解决方案1】:

    您可以使用 awk 脚本:

    script.awk:

    BEGIN { FS="[\t\v]" }
          { for(i=2 ; i <= NF; i++ ) printf("%s\t%s\n",$1,$i) }
    

    像这样:awk -f script.awk yourfile

    (我没有在大型数据集上尝试过这个,我真的很感兴趣它与其他解决方案相比的表现。)

    【讨论】:

    • 在 \t 之前 sed 将 \v 替换为 \t 并注释掉 script.awk 中的第一行可能是一个优势,以便使用固定字符串完成拆分(tab) 而不是正则表达式。
    • 在使用 awk 时永远不需要 sed。字段拆分始终使用正则表达式完成,而不是字符串 - 在字段拆分发生之前,任何指定的字符串都会转换为正则表达式。调整:OFS="\t" ... print $1, $i.
    【解决方案2】:

    这是 Python 中的一个实现(在 3.5 中测试)。我还没有在大型数据集上尝试过这个,我会留给你试试。

    import uuid
    
    in_filename = 'test.txt'
    out_filename = 'parsed.txt'
    
    with open(in_filename) as f_in:
        with open(out_filename, 'w') as f_out:
            for line in f_in:
                try:
                    # Retrieve the line and split into UUID and data
                    line_uuid, data = line.split('\t', maxsplit=1)
                    # Validate UUID
                    uuid.UUID(line_uuid)
                except ValueError:
                    # Ignore this line
                    continue
                # Write each individual piece of data to a separate line
                for data_part in data.rstrip().split('\x0b'):
                    f_out.write(line_uuid + '\t' + data_part  + '\n')
    

    【讨论】:

      【解决方案3】:

      这是一个 awk 脚本,它也将检查 uuid。

      它会忽略没有有效 uuid 的行。

      BEGIN { FS="\v"; OFS="\t" }
      {
        split($1,a,/\s+/);
        if (match(a[1], /^[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}$/, m))
        {
          print a[1],a[2];
          for (i=2;i<=NF;i++) print a[1],$i;
        }
      }
      

      在以下格式的小文件上进行测试:
      uuid (普通标签) data1 (垂直标签) data2 ... (垂直标签) dataN

      如果您确定 uuid 已经有效,那么删除 if 自然会加快速度,因为正则表达式匹配需要一些时间。但可能文件系统的速度可能会成为更大的瓶颈。

      $ awk -f unpivot_data.awk input.txt > result.txt
      
      $ cat result.txt
      abcd1234-ab12-ab12-ab12-abcdef123456    data1
      abcd1234-ab12-ab12-ab12-abcdef123456    data2
      

      老实说,我希望在您测试了不同的解决方案后,您可以与我们分享处理如此庞大的文件的速度有多快/多慢。

      【讨论】:

        猜你喜欢
        • 2018-06-04
        • 2021-04-09
        • 2017-03-11
        • 2021-02-04
        • 2020-07-03
        • 1970-01-01
        • 2019-04-09
        • 2010-12-31
        • 2010-10-31
        相关资源
        最近更新 更多