Regex 基于第二个文本文件从文本文件中删除重复项

Regex 基于第二个文本文件从文本文件中删除重复项,regex,bash,unix,text,editor,Regex,Bash,Unix,Text,Editor,如何通过检查第二个文本文件(removethese.txt)来删除文本文件(main.txt)中的所有行。如果文件大于10-100mb,什么是有效的方法。[使用mac] 例子: 拆下这些线 removethese.txt 3 2 9 输出: output.txt 1 5 示例行(这些是我正在使用的实际行-顺序无关紧要): 使用grep: grep -vxFf removethese.txt main.txt >output.txt fgrep -vxf removethese.txt

如何通过检查第二个文本文件(
removethese.txt
)来删除文本文件(
main.txt
)中的所有行。如果文件大于10-100mb,什么是有效的方法。[使用mac]

例子: 拆下这些线

removethese.txt
3
2
9
输出:

output.txt
1
5
示例行(这些是我正在使用的实际行-顺序无关紧要):


使用
grep

grep -vxFf removethese.txt main.txt >output.txt
fgrep -vxf removethese.txt main.txt >output.txt
awk 'FNR==NR {a[$0];next} !($0 in a)' removethese.txt main.txt >output.txt
sed "s=^=/^=;s=$=$/d=" removethese.txt | sed -f- main.txt >output.txt
使用
fgrep

grep -vxFf removethese.txt main.txt >output.txt
fgrep -vxf removethese.txt main.txt >output.txt
awk 'FNR==NR {a[$0];next} !($0 in a)' removethese.txt main.txt >output.txt
sed "s=^=/^=;s=$=$/d=" removethese.txt | sed -f- main.txt >output.txt
fgrep
已弃用<代码>fgrep--help显示:

不推荐调用为“fgrep”;改用“grep-F”

使用
awk
(来自@fedorqui):

grep -vxFf removethese.txt main.txt >output.txt
fgrep -vxf removethese.txt main.txt >output.txt
awk 'FNR==NR {a[$0];next} !($0 in a)' removethese.txt main.txt >output.txt
sed "s=^=/^=;s=$=$/d=" removethese.txt | sed -f- main.txt >output.txt
使用
sed

grep -vxFf removethese.txt main.txt >output.txt
fgrep -vxf removethese.txt main.txt >output.txt
awk 'FNR==NR {a[$0];next} !($0 in a)' removethese.txt main.txt >output.txt
sed "s=^=/^=;s=$=$/d=" removethese.txt | sed -f- main.txt >output.txt
如果removethese.txt包含特殊字符,则此操作将失败。为此,您可以:

sed 's/[^^]/[&]/g; s/\^/\\^/g' removethese.txt >newremovethese.txt
并在
sed
命令中使用此newremovethese.txt。但这不值得努力,与其他方法相比,它太慢了


对上述方法进行的测试:

grep -vxFf removethese.txt main.txt >output.txt
fgrep -vxf removethese.txt main.txt >output.txt
awk 'FNR==NR {a[$0];next} !($0 in a)' removethese.txt main.txt >output.txt
sed "s=^=/^=;s=$=$/d=" removethese.txt | sed -f- main.txt >output.txt
sed
方法花费太多时间,不值得测试

使用的文件:

removethese.txt : Size: 15191908 (15MB)     Blocks: 29672   Lines: 100233
main.txt : Size: 27640864 (27.6MB)      Blocks: 53992   Lines: 180034
命令:
grep-vxFf
|
fgrep-vxf
|
awk

所用时间:
0m7.966s
|
0m7.823s
|
0m0.237s

0m7.877s
|
0m7.889s
|
0m0.241s

0m7.971s
|
0m7.844s
|
0m0.234s

0m7.864s
|
0m7.840s
|
0m0.251s

0m7.798s
|
0m7.672s
|
0m0.238s

0m7.793s
|
0m8.013s
|
0m0.241s

平均值
0m7.8782s
0m7.8468s
0m0.2403s

该测试结果表明
fgrep
grep
快一点

awk
方法(来自@fedorqui)以优异的成绩通过了测试(
0.2403秒
仅!!!)。

测试环境:

HP ProBook 440 G1 Laptop
8GB RAM
2.5GHz processor with turbo boost upto 3.1GHz
RAM being used: 2.1GB
Swap being used: 588MB
RAM being used when the grep/fgrep command is run: 3.5GB
RAM being used when the awk command is run: 2.2GB or less
Swap being used when the commands are run: 588MB (No change)
测试结果:

grep -vxFf removethese.txt main.txt >output.txt
fgrep -vxf removethese.txt main.txt >output.txt
awk 'FNR==NR {a[$0];next} !($0 in a)' removethese.txt main.txt >output.txt
sed "s=^=/^=;s=$=$/d=" removethese.txt | sed -f- main.txt >output.txt

使用
awk
方法。

有两种标准方法:

使用
grep

grep -vxFf removethese main
它使用:

  • -v
    以反转匹配
  • -x
    匹配整行,以防止,例如,
    匹配像
    你好
    地狱公路
    这样的行
  • -F
    使用固定字符串,使参数保持原样,而不是解释为正则表达式
  • -f
    从另一个文件中获取图案。在这种情况下,请从
    中删除这些
使用
awk

$ awk 'FNR==NR {a[$0];next} !($0 in a)' removethese main
1
5
像这样,我们将每一行存储在
中,并将它们移除到数组
a[]
中。然后,我们读取
main
文件,只打印数组中不存在的行。

我喜欢@fedorqui使用awk进行设置,在设置中有足够的内存来容纳所有“删除这些”行:内存方法的简洁表达

但是,对于要删除的行的大小相对于当前内存来说是很大的场景,并且将该数据读入内存中的数据结构是失败或颤动的邀请,请考虑一种古老的方法:排序/加入

sort main.txt > main_sorted.txt
sort removethese.txt > removethese_sorted.txt

join -t '' -v 1 main_sorted.txt removethese_sorted.txt > output.txt
注:

  • 这不会保留main.txt中的顺序:output.txt中的行将被排序
  • 它需要有足够的磁盘来让sort完成它的工作(临时文件),并存储相同大小的输入文件的排序版本
  • 让join的-v选项做我们想做的事情——从文件1中打印“不成对”,删除匹配项——有点意外
  • 它不直接处理区域设置、排序、键等——它依赖于sort和join(-t,带一个空参数)的默认值来匹配排序顺序,而排序顺序恰好在我当前的机器上工作

    • 以下是我发现的许多简单有效的解决方案:

      您需要使用
      Set complete
      bash命令之一。100MB的文件可以在几秒钟或几分钟内解决

      设置成员资格

      $ grep -xc 'element' set    # outputs 1 if element is in set
                                  # outputs >1 if set is a multi-set
                                  # outputs 0 if element is not in set
      
      $ grep -xq 'element' set    # returns 0 (true)  if element is in set
                                  # returns 1 (false) if element is not in set
      
      $ awk '$0 == "element" { s=1; exit } END { exit !s }' set
      # returns 0 if element is in set, 1 otherwise.
      
      $ awk -v e='element' '$0 == e { s=1; exit } END { exit !s }'
      
      设置相等值

      $ diff -q <(sort set1) <(sort set2) # returns 0 if set1 is equal to set2
                                          # returns 1 if set1 != set2
      
      $ diff -q <(sort set1 | uniq) <(sort set2 | uniq)
      # collapses multi-sets into sets and does the same as previous
      
      $ awk '{ if (!($0 in a)) c++; a[$0] } END{ exit !(c==NR/2) }' set1 set2
      # returns 0 if set1 == set2
      # returns 1 if set1 != set2
      
      $ awk '{ a[$0] } END{ exit !(length(a)==NR/2) }' set1 set2
      # same as previous, requires >= gnu awk 3.1.5
      
      $ wc -l set | cut -d' ' -f1    # outputs number of elements in set
      
      $ wc -l < set
      
      $ awk 'END { print NR }' set
      
      设置交点

      $ cat set1 set2     # outputs union of set1 and set2
                          # assumes they are disjoint
      
      $ awk 1 set1 set2   # ditto
      
      $ cat set1 set2 ... setn   # union over n sets
      
      $ cat set1 set2 | sort -u  # same, but assumes they are not disjoint
      
      $ sort set1 set2 | uniq
      
      # sort -u set1 set2
      
      $ awk '!a[$0]++'           # ditto
      
      $ comm -12 <(sort set1) <(sort set2)  # outputs insersect of set1 and set2
      
      $ grep -xF -f set1 set2
      
      $ sort set1 set2 | uniq -d
      
      $ join <(sort -n A) <(sort -n B)
      
      $ awk 'NR==FNR { a[$0]; next } $0 in a' set1 set2
      
      设置笛卡尔积

      $ comm -23 <(sort subset | uniq) <(sort set | uniq) | head -1
      # outputs something if subset is not a subset of set
      # does not putput anything if subset is a subset of set
      
      $ awk 'NR==FNR { a[$0]; next } { if !($0 in a) exit 1 }' set subset
      # returns 0 if subset is a subset of set
      # returns 1 if subset is not a subset of set
      
      $ comm -23 <(sort set1) <(sort set2)
      # outputs elements in set1 that are not in set2
      
      $ grep -vxF -f set2 set1           # ditto
      
      $ sort set2 set2 set1 | uniq -u    # ditto
      
      $ awk 'NR==FNR { a[$0]; next } !($0 in a)' set2 set1
      
      $ p() { [ $# -eq 0 ] && echo || (shift; p "$@") |
              while read r ; do echo -e "$1 $r\n$r"; done }
      $ p `cat set`
      
      # no nice awk solution, you are welcome to email me one:
      # peter@catonmat.net
      
      $ while read a; do while read b; do echo "$a, $b"; done < set1; done < set2
      
      $ awk 'NR==FNR { a[$0]; next } { for (i in a) print i, $0 }' set1 set2
      
      $ comm -12 <(sort set1) <(sort set2)  # does not output anything if disjoint
      
      $ awk '++seen[$0] == 2 { exit 1 }' set1 set2 # returns 0 if disjoint
                                               # returns 1 if not
      
      $ wc -l < set            # outputs 0  if the set is empty
                               # outputs >0 if the set is not empty
      
      $ awk '{ exit 1 }' set   # returns 0 if set is empty, 1 otherwise
      
      $whilereada;边读边做;做回显“$a$b”;完成
      不相交集检验

      $ comm -23 <(sort subset | uniq) <(sort set | uniq) | head -1
      # outputs something if subset is not a subset of set
      # does not putput anything if subset is a subset of set
      
      $ awk 'NR==FNR { a[$0]; next } { if !($0 in a) exit 1 }' set subset
      # returns 0 if subset is a subset of set
      # returns 1 if subset is not a subset of set
      
      $ comm -23 <(sort set1) <(sort set2)
      # outputs elements in set1 that are not in set2
      
      $ grep -vxF -f set2 set1           # ditto
      
      $ sort set2 set2 set1 | uniq -u    # ditto
      
      $ awk 'NR==FNR { a[$0]; next } !($0 in a)' set2 set1
      
      $ p() { [ $# -eq 0 ] && echo || (shift; p "$@") |
              while read r ; do echo -e "$1 $r\n$r"; done }
      $ p `cat set`
      
      # no nice awk solution, you are welcome to email me one:
      # peter@catonmat.net
      
      $ while read a; do while read b; do echo "$a, $b"; done < set1; done < set2
      
      $ awk 'NR==FNR { a[$0]; next } { for (i in a) print i, $0 }' set1 set2
      
      $ comm -12 <(sort set1) <(sort set2)  # does not output anything if disjoint
      
      $ awk '++seen[$0] == 2 { exit 1 }' set1 set2 # returns 0 if disjoint
                                               # returns 1 if not
      
      $ wc -l < set            # outputs 0  if the set is empty
                               # outputs >0 if the set is not empty
      
      $ awk '{ exit 1 }' set   # returns 0 if set is empty, 1 otherwise
      

      $comm-12目标是您添加一些自己的代码,以显示您自己为解决此问题所做的研究工作。请查看
      grep
      的手册页。@NorbertvanNobelen我尝试了几种解决方案,包括以下解决方案:,但我的列表很大且未排序。但该解决方案不过滤列表。通过Windows、Linux和Mac上提供的文本编辑器UltraEdit,可以使用UE脚本完成此操作,请参阅。@mofi UltraEdit是否处理大文件(10-100mb文件)?@shellter您介意建议一种更简单的方法吗?@shellter yap我测试了它(它可以正常工作)。。。但是你的建议非常有效,只需一行命令就可以完成..太棒了。。谢谢…如果你原来的解决方案有效,你应该把它留在你的答案里。我怀疑
      echo$(grep something output.txt)>output.txt
      是否会覆盖output.txt的现有内容,但时间太晚了,可能会起作用。在任何情况下,我都会推荐
      grep-vFf…
      解决方案。祝大家好运@Sheller即使有效,越晚越好。为了澄清,之前的grep覆盖了内容,这是有意的…@sheller否,output.txt被修改(
      sed-i
      )。它不会将任何输出打印到屏幕上,它会删除
      output.txt
      中的那些行。我以前尝试过
      grep-vFf从另一个答案中删除这些行main.txt>output.txt
      ,处理一个7mb的文件需要4-6个小时。你认为awk是什么