【问题标题】:Deleting lines from a file with binary pattern strings从具有二进制模式字符串的文件中删除行
【发布时间】:2018-05-30 18:06:58
【问题描述】:

我有两个文件。文件 A 包含 N 行的文本,文件 B 包含一个 0 和 1 的二进制模式字符串,长度也为 N。

我想从文件 A 中删除与文件 B 中包含 0 的行号相同的行。

我读到使用 awk 可能是个好主意,但我不知道如何使用它。

文件很长,例如 2000 行(它们是视频轨迹)

例如:

文件 A:

Line 1: 123456
Line 2: 789012
Line 3: 345678
Line 4: 901234

文件 B:

Line 1: 1
Line 2: 0
Line 3: 0
Line 4: 1

执行后:

文件 A:

Line 1: 123456
Line 2: 901234

【问题讨论】:

  • "Line 1:" 等并不是文件的一部分,是吗?
  • 没错,第 1 行等不是我文件的一部分,该行在 : 之后开始(只是为了澄清我想要做什么)。感谢您的所有回答!我今天就开始研究它,并告诉你我最终选择了哪个。很多有趣的建议!!!
  • 而且我希望它对每个文件都执行独立于包含每一行的内容,如果相同的行号在另一个文件上为 0,我希望将其删除,因为视频跟踪会有所不同如果我使用不同的视频。我想你已经考虑过了。

标签: bash shell awk grep


【解决方案1】:

您可以为此使用pastecut

paste fileB fileA | grep '^1' | cut -f2-
  • paste fileB fileA - 并排粘贴文件内容,由制表符分隔
  • grep '^1' - 过滤以 1 开头的行
  • cut -f2- - 提取我们需要的内容

cutpaste 都使用制表符作为默认分隔符。

这与Benjamin's 解决方案非常相似。这里的一个小优势是,即使 fileA 每行有多个字段,它也可以工作。

【讨论】:

  • 好点——如果fileA 中有一个制表符,我的解决方案就会中断,我必须为pastecut 使用不同的分隔符。
  • 终于用过这个了,但是想删行后保存,所以修改为:paste fileB fileA | grep '^1' | cut -f2-> finalFile.txt
【解决方案2】:

这里有很多有趣的答案。这是一个bash:

while IFS= read -r -u3 line; IFS= read -r -u4 bool; do 
    ((bool == 1)) && printf "%s\n" "$line"
done 3<fileA 4<fileB

这将比其他解决方案慢得多。

【讨论】:

    【解决方案3】:

    假设 Line 1: 等在您的输入文件中并不真正存在,您只需要:

    awk 'NR==FNR{a[NR]=$0;next} a[FNR]' fileB fileA
    

    【讨论】:

    • 我在 Windows 上使用过这个,因为我安装了“awk”。也谢谢你!
    【解决方案4】:

    您可以使用decorate – filter – undecorate 模式:

    paste fileA fileB | grep -v '0$' | cut -f1
    

    这将打印每个文件的行并排在一起 (paste),然后过滤以 0 (grep) 结尾的行,然后从第二个文件 (cut) 中删除这些行。

    如果fileA 包含用于pastecut 的分隔符(默认为制表符),则会中断。为避免这种情况,我们可以交换文件(参见codeforester's answer)或诉诸类似

    paste fileA fileB | sed -n '/1$/s/\t.$//p'
    

    (如果行以1 结尾,删除制表符和最后一个字符,然后打印)或

    paste fileA fileB | grep -Po '.*(?=\t1$)'
    

    (仅匹配以1 结尾的行,使用零宽度前瞻从匹配中排除制表符和1);最后一个解决方案需要一个支持 Perl 兼容正则表达式 (PCRE) 的 grep,例如 GNU grep。

    【讨论】:

      【解决方案5】:

      一个awk 命令可以读取这两个文件。

      awk '(getline flag < "fileB") > 0 && flag' fileA
      

      fileA 中读取每一行后,从fileB 中读取一行到变量标志中,并测试其整数值是否为真。对于真值,将打印来自fileA 的行。

      根据您的awk 版本,您可能需要使用int(flag)flag+0 来强制将该值视为整数而不是普通的非空字符串。

      【讨论】:

      • 嗯。我添加了对 int 的调用,因为(至少在我用于测试的 macOS 上的 BSD 版本中)当我单独使用 flag 时,每一行都被打印出来。
      • 我不确定我是否理解getline flag &lt; "fileB"(getline flag &lt; "fileB") &gt; 0 之间的区别。
      • 关于getline,显然我只需要更仔细地阅读文档。谢谢。
      【解决方案6】:

      另一个paste/awk 解决方案。如果选项卡出现在数据中,请查找另一个分隔符。

      paste file2 file1 | awk -F'\t' '$1{print $2}' 
      

      【讨论】:

        【解决方案7】:

        编辑: @codeforester 的评论如果 Line 1Line 2 不是您的 File1 和 File2 的一部分,那么以下可能会有所帮助。

        awk 'FNR==NR{a[FNR]=$0;next} $0!=0{print a[FNR]}' filea fileb
        

        方案二:先读取fileb文件,再读取filea。

        awk 'FNR==NR{if($0!=0){a[FNR]=$0};next} a[FNR]' fileb filea
        


        解决方案 1 的替代方案,以防 OP 在他/她的文件中有字符串 line1、line2。

        关注awk 在这里也可能有所帮助。

        awk '
        FNR==NR{
          a[FNR]=$NF;
          next}
        $NF!=0{
          printf("%s%s\n","Line " ++count": ",a[FNR])
        }' filea fileb
        

        【讨论】:

        • 我认为“第 1 行:”等不是文件的一部分。
        • @codeforester,很酷,现在肯定也添加了 EDIT 解决方案,以防行字符串不是文件的一部分。
        • 这里$NF!=0可以换成$NF
        【解决方案8】:

        pastesed 组合:

        paste -d'\n' fileB fileA | sed -n '/^1$/{n;p}'
        123456
        901234
        

        你交错文件:

        1
        123456
        0
        789012
        0
        345678
        1
        901234
        

        然后您使用sed 打印直接跟在只有1 的行之后的行。但是,如果您的条目仅由 fileA 中的 1 组成,这将无法正常运行。如果是这种情况,那么您必须使用以下sed 命令来考虑我们当前是否正在处理奇数/偶数行:

        paste -d'\n' fileB fileA | sed -n '1~2{/^1$/{n;p}}'
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-12-18
          • 2011-02-21
          • 1970-01-01
          • 1970-01-01
          • 2020-06-21
          • 1970-01-01
          • 1970-01-01
          • 2021-06-20
          相关资源
          最近更新 更多