【问题标题】:Match file names and replace with new name匹配文件名并替换为新名称
【发布时间】:2013-11-10 15:07:33
【问题描述】:

我有两个具有相似 ID 标签的相当大的 .txt 文件。我需要做的是从一个文件中获取 ID 标签,在另一个文件中进行匹配,然后将 ID 替换为第一个文件中的名称。我需要为 1000 多个标签完成此操作。关键是要完全匹配第一个文件中的部分 ID 标签名称并替换它。

  • 每行都有一个唯一的 ID 标签,并且两个文件之间始终存在完全匹配(对于位置 [6-16] = "10737.G1C22");匹配分散,因此 File1.txt 中的第 1 行可能与 File2.txt 中的第 504 行匹配

  • 两个文件中的行顺序不能排序,必须保持

例如:

File1.txt = 
TYPE1_10737.G1C22 ---------
...

File2.txt = 
10737.G1C22 ----------

我需要 File1.txt 中的名称,特别是“10737.G1C22”,以便在 File2.txt 中找到其完全匹配并将其替换为“TYPE1_10737.G1C22”。

然后编辑看起来像这样,现在 File2.txt 中的名称根据来自 File1.txt 的匹配项进行了更改:

 File2.txt = 
 TYPE1_10737.G1C22 ---------
 ...

我尝试了一些 sed 功能,但都卡住了。重要的是,一旦找到完全匹配的名称,只有名称的前 6 个字符会被更改,而不是其他任何字符。有超过 1000 多个 ID 标签需要匹配和更改。

我正在考虑告诉它精确匹配位置 [6-16] 并将其替换为 File1.txt 中的 [0-16] 的代码。

非常感谢任何帮助。这甚至可能吗?我也愿意接受其他建议。谢谢你。

【问题讨论】:

  • 每行一个ID,总是在每个文件的行首?对于每个 file2 ID,file1 中最多有一个匹配项吗?
  • 是的。每行一个 ID,是的,文件之间至少有一个匹配项。谢谢!
  • 一种方法是从 file1 生成一个字典(我看到你的 python 标签),其中键是要在 file2 中找到的匹配项,字典值是全名。然后读取file2,在字典中查找,并将行重写为一个新文件。
  • file2 中的行顺序重要吗?即它和file1可以排序

标签: python bash shell python-2.7 notepad++


【解决方案1】:

python中的一个简单解决方案:

from collections import OrderedDict
LINES_PER_CYCLE = 1000

with open('output.txt', 'wb') as output, open('test_2.txt', 'rb') as fin:
    fin_line = ''

    # Loop until fin reaches EOF.
    while True:
        cache = OrderedDict()

        # Fill the cache with up to LINES_PER_CYCLE entries.
        for _ in xrange(LINES_PER_CYCLE):
            fin_line = fin.readline()
            if not fin_line:
                break

            key, rest = fin_line.strip().split(' ', 1)
            cache[key] = ['', rest]

        # Loop over the file_1.txt to find tags with given id.    
        with open('test_1.txt', 'rb') as fout:
            for line in fout:
                tag, _ = line.split(' ', 1)
                _, idx = tag.rsplit('_', 1)
                if idx in cache:
                    cache[idx][0] = tag

        # Write matched lines to the output file, in the same order
        # as the lines were inserted into the cache.
        for _, (tag, rest) in cache.iteritems():
            output.write('{} {}\n'.format(tag, rest))

        # If fin has reached EOF, break.    
        if not fin_line:
            break

它所做的是从file_2.txt 中读取最多LINES_PER_CYCLE 条目,在file_1.txt 中查找匹配的条目并写入输出。由于内存有限(用于缓存),file_1.txt 被多次搜索。

这假定标签/id 部分由空格与------- 分隔,并且标签和id 由下划线分隔,即。 'tag_idx blah blah'。

【讨论】:

    【解决方案2】:

    Bash 和ed 解决方案

    • 第 1 步。创建 File1.txtFile2.txt,它们或多或少看起来像您的实验并获得一些乐趣(有 1000 行)。使用此脚本(在临时目录中):

      #!/bin/bash
      
      declare -A table
      
      while ((${#table[@]}!=1000)); do
          key=$(mktemp -u XXXXXXXXXX)
          key=${key:0:5}.${key:5}
          table[${key^^}]=1
      done
      
      {
          for key in "${!table[@]}"; do
              echo "TYPE1_$key some junk here" >&3
              echo "$key some more junk here"
          done | shuf > File2.txt
      } 3> File1.txt
      
    • 第 2 步。使用标准编辑器 ed 进行替换,并包含在此脚本中:

      #!/bin/bash
      
      ed -s File2.txt < <(
         while read l _; do
            p=${l:6}
            p=${p//./\\.}
            echo "%s/^$p/$l/"
         done < File1.txt
         echo wq
      )
      

      这假设您只有字母数字字符、下划线_ 和句点.。如果您有其他字符,请适当修改(以免与正则表达式冲突)。

    • 第 3 步。检查并享受:

      vimdiff <(sort File1.txt) <(sort File2.txt)
      

    完成。

    注意。由于ed是正版编辑器,替换已经到位。 File2.txt 真的被编辑过


    嘿,等一下,我可能忽略了您对 16 个字符的要求...我使用了您的模式后面有一个空格的事实。如果我的解决方案在这一点上不好,请告诉我,我会适当修改。

    【讨论】:

      【解决方案3】:

      基于 Python 的解决方案很简单,但请注意,这不能就地完成,您必须将结果存储到某个新位置,例如 tempfile

      如果您的文件不是太大,即您可以在内存中构建映射,则很简单(假设 1)名称与 id 用下划线分隔,2)id 与文本用空格分隔,如示例3) 每行包含 id 和 name 4) 每个 id 只存在一个 name 文件 1):

      file1 = ('TYPE1_10737.G1C22 ---------', )
      file2 = ('10737.G1C22 +++++++++++', )
      id_name_gen = (l.split(' ', 1)[0] for l in file1)
      id2name_mapping = {line.split('_', 1)[1]: line for line in id_name_gen}
      

      一旦你有一个映射就可以很容易地完成替换(如果没有找到匹配,保持字符串不变):

      id_rest_gen = (l.split(' ', 1) for l in file2)
      file2updated_gen = ('{} {}'.format(id2name_mapping.get(id, id), rest) for id, rest in file2)
      
      >>> list(file2updated_gen)
      ['TYPE1_10737.G1C22 +++++++++++']
      

      您只需将生成的生成器存储到文件中。

      【讨论】:

        【解决方案4】:

        我会将第一个文件加载到字典中,然后处理第二个文件以匹配键,将任何更改输出到第三个文件:

        import re
        
        # Pattern to match in File1
        pattern1 = "(\w+)_(\d+\.\w+)\s+.*$"
        
        # Pattern to match in File2
        pattern2 = "(\d+\.\w+)\s+.*$"
        
        # Load the 'master' file into a dict,
        # with the number as key and 'type' as value.
        file1_dict = dict()
        with open("File1.txt", "r") as f:
            for line in f.readlines():
                m = re.match(pattern1, line)
                if m:
                    file1_dict[m.group(2)] = m.group(1)
        
        # Open a new output file to replace File2.txt
        with open("File3.txt", "w") as fnew:
            # As you process each line in File2.txt,
            # find matching entry in above File1 list.
            # Either write the old unmatched value or new
            # matching, changed value to File3.txt
            with open("File2.txt", "r") as f:
                for line in f.readlines():
                    is_found = False
                    m = re.match(pattern2, line)
                    if m:
                        if m.group(1) in file1_dict:
                            is_found = True
                            fnew.write("{0}_{1}".format(file1_dict[m.group(1)], line))
                    if not is_found:
                        fnew.write(line)
        
        # Then just overwrite File2.txt with new File3.txt contents.
        
        # Original File1.txt
        TYPE1_10737.G1C22 ---------
        TYPE1_10738.G1C22 ---------
        TYPE1_10739.G1C22 ---------
        TYPE1_10740.G1C22 ---------
        TYPE1_10741.G1C22 ---------
        TYPE1_10742.G1C22 ---------
        TYPE1_10799.G1C22 ---------
        
        # Original File2.txt
        10737.G1C22 ---------
        10738.G1C22 ---------
        10739.G1C22 ---------
        10740.G1C22 ---------
        10788.G1C22 ---------
        10741.G1C22 ---------
        10742.G1C22 ---------
        
        # Results of new File3.txt
        TYPE1_10737.G1C22 ---------
        TYPE1_10738.G1C22 ---------
        TYPE1_10739.G1C22 ---------
        TYPE1_10740.G1C22 ---------
        10788.G1C22 ---------
        TYPE1_10741.G1C22 ---------
        TYPE1_10742.G1C22 ---------
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-06-24
          • 1970-01-01
          • 2014-04-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-12-06
          • 1970-01-01
          相关资源
          最近更新 更多