2013-08-20 3 views
1

У меня есть файл csv и другой текстовый файл (скажем file1.csv и file2.txt). Текстовый файл имеет один столбец. Теперь я хочу отфильтровать файл csv на основе данных в текстовом файле. Например,Фильтрация CSV-файлов

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

file2.txt 
----------- 
1 
3 

Я хочу, чтобы результат быть-

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

Ok. Я смог решить это с помощью простой команды grep. grep -f file2.txt file1.csv. Мне просто интересно узнать, есть ли другие способы. – Teja

+4

Ваше приятное простое решение также подберет строки '10, p, q, r' и' 300, x, y, z' из 'file1.csv' (не говоря уже о' 444,1,2,33') ; это нормально? Если нет, вам нужно использовать команду «join» с соответствующими типами, вероятно (или 'awk' и ассоциативными массивами, или Perl, или Python, или, может быть,' grep -f <(sed 's /.*/^& ,/'file2.txt) file1.csv'). –

+5

Вы также можете просмотреть [csvfix] (http://code.google.com/p/csvfix) для инструмента для управления файлами CSV. –

ответ

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 
1

Для решения с использованием awk, см. answer by kev.

Для улучшенного решения с использованием grep -f, рассмотреть вопрос об использовании bashprocess substitution:

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

Это использует sed на линиях в file2.txt поставить каретку в начале каждой строки и запятой в конце, так что при обработке в качестве регулярного выражения (GNU?) grep шаблоны соответствуют только точной величине поля в начале строки. Если у вас нет bash, вы можете быть в состоянии использовать:

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

Однако не все версии grep читает стандартный ввод при указании -f - (версия на Mac OS X не, например, но GNU grep делает).

В качестве альтернативы, вы можете использовать команду 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 (awk), но из-за того, что два файла нужно обрабатывать по-разному, это довольно сложно, чтобы заставить его работать правильно.

Из этого, я думаю, решение grep -f, вероятно, является самым простым, поскольку file2.txt не слишком большой (и я не уверен, какой предел был бы - но, вероятно, удивительно большой).

Для инструмента обработки файлов CSV общего назначения рассмотрите csvfix.

0

Попробуйте следующую команду:

Grep -F -f file2.txt file1.csv

1, а, б, в
3, г, д, г

0

И для Windows Command Version:

findstr /G:file2.txt file1.csv > result.csv