1

我有一个 csv 文件和另一个文本文件(比如 file1.csv 和 file2.txt)。文本文件有一列。现在,我想根据文本文件中的数据过滤 csv 文件。例如,

文件 1.csv
------------
1,a,b,c
2,d,e,f
3,g,d,g

文件2.txt
------------
1
3

我希望结果是——

1,a,b,c
3,g,d,g
4

4 回答 4

2

试试这个命令:

awk -F, 'FNR==NR{a[$0];next};$1 in a' file2.txt file1.csv

逻辑很简单:

FOR each line in 'file2.txt' and 'file1.csv'
    IF line is from 'file2.txt'
        store it to array 'a'
        CONTINUE
    ENDIF
    IF column 1 of line is in 'a'
        PRINT line
    ENDIF
ENDFOR
于 2013-08-21T00:27:26.357 回答
1

有关使用 的解决方案awk,请参阅kev答案

对于使用 的改进解决方案grep -f,请考虑使用bash 过程替换

grep -f <(sed 's/.*/^&,/' file2.txt) file1.csv

这用于sedfile2.txt每行的开头放置一个插入符号并在末尾放置一个逗号,这样当 (GNU?) 将其视为正则表达式时grep,模式仅匹配行开头的确切字段值. 如果您没有bash,您可以使用:

sed 's/.*/^&,/' file2.txt | grep -f - file1.csv

但是,当您指定时,并非所有版本都grep可以读取标准输入-f -(例如,Mac OS X 上的版本不会,但 GNUgrep会)。

或者,您可以使用join适当排序的命令:

join -o 1.1,1.2,1.3,1.4 -t, <(sort file1.csv) <(sort file2.txt)

如果您确信文件已经排序,您可以将其简化为:

join -o 1.1,1.2,1.3,1.4 -t, file1.csv file2.txt

在 Perl 中,您可以使用:

#!/usr/bin/env perl
use strict;
use warnings;

my $file = 0;
my %rows;

while (<>)
{
    chomp;
    $rows{$_}++ if ($file == 0);
    if ($file == 1)
    {
        my($id) = split /,/;
        print "$_\n" if defined $rows{$id};
    }
}
continue
{
    $file = 1 if eof;
}

可能还有其他方法可以做到这一点;例如,您可能会发现Text::CSV等模块的用途。

但是,此代码读取每一行。如果它来自第一个文件,那么它会创建一个条目$rows{$_}++来记录看到的数字。顺序和重复无关紧要。在第二个(和后续)文件中,它将第一个逗号分隔的字段从行中拆分出来,并检查是否在第一个文件中找到了该数字;如果是这样,它会打印整行。该continue块检测代码何时到达第一个文件的 EOF(特别是)并设置$file = 1;何时到达。它与awk解同构。这有点冗长。有-a模式(awkmode),但是因为这两个文件需要区别对待,让它正常工作有点棘手。

其中,我认为grep -f解决方案可能是最简洁的,只要file2.txt不太大(我不确定限制是多少——但可能大得惊人)。

对于通用 CSV 文件操作工具,请考虑csvfix

于 2013-08-21T04:26:45.007 回答
0

对于 Windows 命令版本:

findstr /G:file2.txt file1.csv > result.csv
于 2014-02-10T09:18:50.977 回答
0

试试下面的命令:

grep -F -f file2.txt file1.csv

1,a,b,c
3,g,d,g

于 2013-08-21T05:56:53.263 回答