【问题标题】:Compare strings from a txt with bash or python ignoring pattern将 txt 中的字符串与 bash 或 python 忽略模式进行比较
【发布时间】:2020-10-27 04:15:35
【问题描述】:

我想在 txt 文件中搜索不包括 [p] 的重复行和比较中的扩展名。一旦确定了相等的行,只显示不包含 [p] 的行及其扩展名。我在 test.txt 中有这行:

Peliculas/Desperados (2020)[p].mp4
Peliculas/La Duquesa (2008)[p].mp4
Peliculas/Nueva York Año 2012 (1975).mkv
Peliculas/Acoso en la noche (1980) .mkv
Peliculas/Angustia a Flor de Piel (1982).mkv
Peliculas/Desperados (2020).mkv
Peliculas/Angustia (1947).mkv
Peliculas/Días de radio (1987) BR1080[p].mp4
Peliculas/Mona Lisa (1986) BR1080[p].mp4
Peliculas/La decente (1970) FlixOle WEB-DL 1080p [Buzz][p].mp4
Peliculas/Mona Lisa (1986) BR1080.mkv

在这个文件中,第 1-6 行和第 9-11 行是相同的(没有 ext 和 [p])。需要输出:

Peliculas/Desperados (2020).mkv
Peliculas/Mona Lisa (1986) BR1080.mkv

我试试这个,但只显示删除扩展名和模式 [p] 的相同行,但我不知道正确的行,我需要整行完成

sed 's/\[p\]//' ./test.txt | sed 's\.[^.]*$//' | sort | uniq -d

错误输出(缺少扩展名):

Peliculas/Desperados (2020)
Peliculas/Mona Lisa (1986) BR1080

【问题讨论】:

    标签: python string bash search compare


    【解决方案1】:

    因为你提到了 bash...

    删除任何带有p 的行:

    cat test.txt | grep -v p                     
    home/folder/house from earth.mkv
    home/folder3/window 1.avi
    

    删除任何带有[p]的行:

    cat test.txt | grep -v '\[p\]'
    home/folder/house from earth.mkv
    home/folder3/window 1.avi
    home/folder4/little mouse.mpg
    

    不太可能是您的需要,只是因为:从每一行中删除 [p],然后进行重复数据删除:

    cat test.txt | sed 's/\[p\]//g' | sort | uniq
    home/folder/house from earth.mkv
    home/folder/house from earth.mp4
    home/folder2/test.mp4
    home/folder3/window 1.avi
    home/folder3/window 1.mp4
    home/folder4/little mouse.mpg 
    

    【讨论】:

      【解决方案2】:

      在 Python 中,您可以将 itertools.groupby 与一个函数一起使用,该函数生成一个由文件名组成的密钥,不带任何 [p] 并删除扩展名。

      对于任何大小为 2 或更大的组,将打印任何不包含 '[p]' 的文件名。

      import itertools
      import re
      
      def make_key(line):
          return re.sub(r'\.[^.]*$', '', line.replace('[p]', ''))
      
      with open('test.txt') as f:
          lines = [line.strip() for line in f]
      
      for key, group in itertools.groupby(lines, make_key):
          files = [file for file in group]
          if len(files) > 1:
              for file in files:
                  if '[p]' not in file:
                      print(file)
      

      这给出了:

      home/folder/house from earth.mkv
      home/folder3/window 1.avi
      

      【讨论】:

      • 它适用于这个例子,但对于我的最终文件它不起作用。返回没有带有 [p] 的重复行的结果。我不知道为什么。我的txt有12000行
      • @Sirfrancis18 请举例说明它的作用与您的要求不同,以便我更好地理解您的要求。
      • 我附上了我文件的一部分。在这个文件中,只有文件 1-6 和 9-11 相等,而 q 和 ext 不同。 file.io/yDMd0qfA
      • 我不明白为什么不工作。结构是一样的
      • @Sirfrancis18 您的链接给出了 404,无论如何,示例输出与行本身一样重要。请编辑问题以准确地说明以下标准:(a) 如何确定什么被视为“重复”,以及 (b) 应打印哪些文件名以及应排除哪些文件名。如果您这样做,然后在此处添加评论以提醒我,那么希望我下次会看。
      【解决方案3】:

      如果 2-pass 解决方案(读取 test.txt 文件两次)是可以接受的,请尝试一下:

      declare -A ary                          # associate the filename with the base
      while IFS= read -r file; do
          if [[ $file != *\[p\]* ]]; then     # the filename does not include "[p]"
              base="${file%.*}"               # remove the extension
              ary[$base]="$file"              # create a map
          fi
      done < test.txt
      
      while IFS= read -r base; do
          echo "${ary[$base]}"
      done < <(sed 's/\[p\]//' ./test.txt | sed 's/\.[^.]*$//' | sort | uniq -d)
      

      输出:

      Peliculas/Desperados (2020).mkv
      Peliculas/Mona Lisa (1986) BR1080.mkv
      
      • 在第一遍中,它逐行读取文件以创建将文件名(带扩展名)与基本文件名(不带扩展名)相关联的映射。
      • 在第二遍中,它将输出(基础)替换为文件名。

      如果您更喜欢 1-pass 解决方案(会更快),请尝试:

      declare -A ary                  # associate the filename with the base
      declare -A count                # count the occurrences of the base
      while IFS= read -r file; do
          base="${file%.*}"           # remove the extension
          if [[ $base =~ (.*)\[p\](.*) ]]; then
                                      # "$base" contains the substring "[p]"
              (( count[${BASH_REMATCH[1]}${BASH_REMATCH[2]}]++ ))
                                      # increment the counter
          else
              (( count[$base]++ ))    # increment the counter
              ary[$base]="$file"      # map the filename
          fi
      done < test.txt
      
      for base in "${!ary[@]}"; do    # loop over the keys of ${ary[@]}
          if (( count[$base] > 1 )); then
                                      # it duplicates
              echo "${ary[$base]}"
          fi
      done
      

      【讨论】:

      • 2-pass 解决方案有效。另一个不要。我不知道为什么
      • @Sirfrancis18 感谢您的反馈。我也不知道为什么:-/。你的 bash 版本是哪个?无论如何,请享受 2-pass 解决方案。
      猜你喜欢
      • 2014-07-15
      • 2022-01-06
      • 1970-01-01
      • 2018-03-17
      • 2021-03-08
      • 2016-02-12
      • 1970-01-01
      • 2015-08-14
      • 2021-03-04
      相关资源
      最近更新 更多